+ * Defines number of channels and meaning of each channel. + */ +public enum ChannelLayout { + + /** + * Multi-track audio. + *
+ * In multitrack layout, stream contains multiple channels which represent independent + * "tracks" without any special meaning (unlike stereo or surround) and hence without any + * special processing or mapping. + *
+ * The number of channels is arbitrary and is defined by {@code tracks} field of + * {@link MediaEncoding} struct. + */ + MULTITRACK(1), + + /** + * Mono. + *
+ * One channel with monophonic sound. + */ + MONO(2), + + /** + * Stereo. + *
+ * Two channels: left, right. + */ + STEREO(3), + ; + + final int value; + + ChannelLayout(int value) { + this.value = value; + } +} diff --git a/src/main/java/org/rocstreaming/roctoolkit/ChannelSet.java b/src/main/java/org/rocstreaming/roctoolkit/ChannelSet.java deleted file mode 100644 index 38b0bf15..00000000 --- a/src/main/java/org/rocstreaming/roctoolkit/ChannelSet.java +++ /dev/null @@ -1,24 +0,0 @@ -// DO NOT EDIT! Code generated by generate_enums script from roc-toolkit -// roc-toolkit git tag: v0.2.5-11-g14d642e9, commit: 14d642e9 - -package org.rocstreaming.roctoolkit; - -/** - * Channel set. - */ -public enum ChannelSet { - - /** - * Stereo. - *
- * Two channels: left and right.
- */
- STEREO(0x3),
- ;
-
- final int value;
-
- ChannelSet(int value) {
- this.value = value;
- }
-}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/Check.java b/src/main/java/org/rocstreaming/roctoolkit/Check.java
index a72e7b42..dbfc74eb 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/Check.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/Check.java
@@ -8,14 +8,14 @@ private Check() {
static
+ * Defines wo is responsible to invoke read or write in proper time.
*/
public enum ClockSource {
/**
* Sender or receiver is clocked by external user-defined clock.
*
- * Write and read operations are non-blocking. The user is responsible to
- * call them in time, according to the external clock.
+ * Write and read operations are non-blocking. The user is responsible to call them in
+ * time, according to the external clock.
+ *
+ * Use when samples source (from where you read them to pass to receiver) or destination
+ * (to where you write them after obtaining from sender) is active and has its own clock,
+ * e.g. it is a sound card.
*/
EXTERNAL(0),
/**
- * Sender or receiver is clocked by an internal clock.
+ * Sender or receiver is clocked by an internal pipeline clock.
+ *
+ * Write and read operations are blocking. They automatically wait until it's time to
+ * process the next bunch of samples according to the configured sample rate, based on a
+ * CPU timer.
*
- * Write and read operations are blocking. They automatically wait until
- * it's time to process the next bunch of samples according to the
- * configured sample rate.
+ * Use when samples source (from where you read them to pass to receiver) or destination
+ * (to where you write them after obtaining from sender) is passive and does now have
+ * clock, e.g. it is a file on disk.
*/
INTERNAL(1),
;
diff --git a/src/main/java/org/rocstreaming/roctoolkit/ClockSyncBackend.java b/src/main/java/org/rocstreaming/roctoolkit/ClockSyncBackend.java
new file mode 100644
index 00000000..498baa2e
--- /dev/null
+++ b/src/main/java/org/rocstreaming/roctoolkit/ClockSyncBackend.java
@@ -0,0 +1,56 @@
+// Code generated by bindgen.py from roc-streaming/bindgen
+// roc-toolkit git tag: v0.3.0, commit: 57b932b8
+
+package org.rocstreaming.roctoolkit;
+
+/**
+ * Clock synchronization algorithm.
+ *
+ * Defines how sender and receiver clocks are synchronized.
+ */
+public enum ClockSyncBackend {
+
+ /**
+ * Disable clock synchronization.
+ *
+ * In this mode, sender and receiver clocks are not synchronized. This mode is generally
+ * not recommended, since clock drift will lead to periodic playback disruptions caused
+ * by underruns and overruns.
+ */
+ DISABLE(-1),
+
+ /**
+ * Default backend.
+ *
+ * Current default is {@link ClockSyncBackend#NIQ}.
+ */
+ DEFAULT(0),
+
+ /**
+ * Clock synchronization based on network incoming queue size.
+ *
+ * In this mode, receiver monitors incoming queue size and adjusts playback clock speed
+ * to match the estimated capture clock speed.
+ *
+ * Pros:
+ *
+ * Cons:
+ *
+ * Defines what latency and jitter are tolerated by clock synchronization algorithm.
+ */
+public enum ClockSyncProfile {
+
+ /**
+ * Default profile.
+ *
+ * When {@link ClockSyncBackend#NIQ} is used, selects {@link ClockSyncProfile#RESPONSIVE}
+ * if target latency is low, and {@link ClockSyncProfile#GRADUAL} if target latency is
+ * high.
+ */
+ DEFAULT(0),
+
+ /**
+ * Responsive clock adjustment.
+ *
+ * Clock speed is adjusted quickly and accurately.
+ *
+ * Requires high precision clock adjustment, hence recommended for use with
+ * {@link ResamplerBackend#BUILTIN}.
+ *
+ * Pros:
+ *
+ * Cons:
+ *
+ * Clock speed is adjusted slowly and smoothly.
+ *
+ * Pros:
+ *
+ * Cons:
+ *
- * Note: It's important that this method is declared
- * Endpoint is a network entry point of a peer. The definition includes the
- * protocol being used, network host and port, and, for some protocols, a
- * resource. All these parts together are unambiguously represented
- * by a URI. The user may set or get the entire URI or its individual parts.
- *
- * Endpoint URI syntax is a subset of the syntax defined in RFC 3986:
- *
- * protocol://host[:port][/path][?query]
- *
- * Examples:
+ * Endpoint URI syntax is a subset of the syntax defined in RFC 3986: Examples:
*
* The following protocols (schemes) are supported:
*
- * The host field should be either FQDN (domain name), or IPv4 address, or
- * IPv6 address in square brackets.
+ * The host field should be either FQDN (domain name), or IPv4 address, or IPv6 address in square
+ * brackets.
*
- * The port field can be omitted if the protocol defines standard port. Otherwise,
- * the port can not be omitted. For example, RTSP defines standard port,
- * but RTP doesn't.
+ * The port field can be omitted if the protocol defines standard port. Otherwise, the port can
+ * not be omitted. For example, RTSP defines standard port, but RTP doesn't.
*
- * The path and query fields are allowed only for protocols that support them.
- * For example, they're supported by RTSP, but not by RTP.
+ * The path and query fields are allowed only for protocols that support them. For example,
+ * they're supported by RTSP, but not by RTP.
+ *
+ *
+ *
- *
- * Can't be used concurrently
+ * Sender and receiver refuse to bind or connect an endpoint which has invalid fields or doesn't
+ * have some mandatory fields. Hence, it is safe to ignore errors returned by endpoint setters
+ * and check only for errors returned by bind and connect operations.
+ *
+ *
+ *
+ * Should not be used concurrently.
*/
@Getter
@Builder(builderClassName = "Builder", toBuilder = true)
@@ -86,12 +94,14 @@ public class Endpoint {
private String resource;
/**
- * Create endpoint from uri
+ * Create endpoint from URI
+ *
+ * @param uri URI to parse
*
- * @param uri uri
+ * @throws IllegalArgumentException if URI is invalid
*/
public Endpoint(String uri) {
- init(uri);
+ nativeParseUri(uri);
}
/**
@@ -107,14 +117,16 @@ public Endpoint(String uri) {
*
* If port is set to -1, the standard port for endpoint protocol is used. This is
* allowed only if the protocol defines its standard port.
- * @param resource resource nullable. Specifies percent-encoded path and query
+ * @param resource resource is nullable. Specifies percent-encoded path and query
+ *
+ * @throws IllegalArgumentException if URI components don't form a valid URI
*/
public Endpoint(Protocol protocol, String host, int port, String resource) {
- this.protocol = Check.notNull(protocol, "protocol");
- this.host = Check.notEmpty(host, "host");
+ this.protocol = Check.notNull(protocol, "endpoint protocol");
+ this.host = Check.notEmpty(host, "endpoint host");
this.port = port;
this.resource = resource;
- validate();
+ nativeValidate();
}
/**
@@ -130,19 +142,29 @@ public Endpoint(Protocol protocol, String host, int port, String resource) {
*
* If port is set to -1, the standard port for endpoint protocol is used. This is
* allowed only if the protocol defines its standard port.
+ *
+ * @throws IllegalArgumentException if URI components don't form a valid URI
*/
public Endpoint(Protocol protocol, String host, int port) {
this(protocol, host, port, null);
}
+ /**
+ * Get string URI describing this endpoint.
+ */
+ public String getUri() {
+ return nativeFormatUri();
+ }
+
+ /**
+ * Get string URI describing this endpoint.
+ */
@Override
public String toString() {
return getUri();
}
- public native String getUri();
-
- private native void init(String uri) throws IllegalArgumentException;
-
- private native void validate() throws IllegalArgumentException;
+ private native void nativeParseUri(String uri);
+ private native String nativeFormatUri();
+ private native void nativeValidate();
}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/FecEncoding.java b/src/main/java/org/rocstreaming/roctoolkit/FecEncoding.java
index 448b72f2..e4e01246 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/FecEncoding.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/FecEncoding.java
@@ -1,10 +1,12 @@
-// DO NOT EDIT! Code generated by generate_enums script from roc-toolkit
-// roc-toolkit git tag: v0.2.5-11-g14d642e9, commit: 14d642e9
+// Code generated by bindgen.py from roc-streaming/bindgen
+// roc-toolkit git tag: v0.3.0, commit: 57b932b8
package org.rocstreaming.roctoolkit;
/**
* Forward Error Correction encoding.
+ *
+ * Each FEC encoding is caompatible with specific protocols.
*/
public enum FecEncoding {
@@ -12,6 +14,16 @@ public enum FecEncoding {
* No FEC encoding.
*
* Compatible with {@link Protocol#RTP} protocol.
+ *
+ * Pros:
+ *
+ * Cons:
+ *
- * Good for small block sizes (below 256 packets). Compatible with {@link
- * Protocol#RTP_RS8M_SOURCE} and {@link Protocol#RS8M_REPAIR} protocols for
- * source and repair endpoints.
+ * Good for small block sizes (below 256 packets). Compatible with
+ * {@link Protocol#RTP_RS8M_SOURCE} and {@link Protocol#RS8M_REPAIR} protocols for source
+ * and repair endpoints.
+ *
+ * Pros:
+ *
+ * Cons:
+ *
- * Good for large block sizes (above 1024 packets). Compatible with {@link
- * Protocol#RTP_LDPC_SOURCE} and {@link Protocol#LDPC_REPAIR} protocols for
- * source and repair endpoints.
+ * Good for large block sizes (above 1024 packets). Compatible with
+ * {@link Protocol#RTP_LDPC_SOURCE} and {@link Protocol#LDPC_REPAIR} protocols for source
+ * and repair endpoints.
+ *
+ * Pros:
+ *
+ * Cons:
+ *
+ * Defines how each sample is represented. Does not define channels layout and sample rate.
+ */
+public enum Format {
+
+ /**
+ * PCM floats.
+ *
+ * Uncompressed samples coded as 32-bit native-endian floats in range [-1; 1]. Channels
+ * are interleaved, e.g. two channels are encoded as "L R L R...".
+ */
+ PCM_FLOAT32(1),
+ ;
+
+ final int value;
+
+ Format(int value) {
+ this.value = value;
+ }
+}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/FrameEncoding.java b/src/main/java/org/rocstreaming/roctoolkit/FrameEncoding.java
deleted file mode 100644
index 775031f6..00000000
--- a/src/main/java/org/rocstreaming/roctoolkit/FrameEncoding.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// DO NOT EDIT! Code generated by generate_enums script from roc-toolkit
-// roc-toolkit git tag: v0.2.5-11-g14d642e9, commit: 14d642e9
-
-package org.rocstreaming.roctoolkit;
-
-/**
- * Frame encoding.
- */
-public enum FrameEncoding {
-
- /**
- * PCM floats.
- *
- * Uncompressed samples coded as floats in range [-1; 1]. Channels are
- * interleaved, e.g. two channels are encoded as "L R L R...".
- */
- PCM_FLOAT(1),
- ;
-
- final int value;
-
- FrameEncoding(int value) {
- this.value = value;
- }
-}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/Interface.java b/src/main/java/org/rocstreaming/roctoolkit/Interface.java
index d7aa8e51..3cbd655c 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/Interface.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/Interface.java
@@ -1,60 +1,55 @@
-// DO NOT EDIT! Code generated by generate_enums script from roc-toolkit
-// roc-toolkit git tag: v0.2.5-11-g14d642e9, commit: 14d642e9
+// Code generated by bindgen.py from roc-streaming/bindgen
+// roc-toolkit git tag: v0.3.0, commit: 57b932b8
package org.rocstreaming.roctoolkit;
/**
* Network interface.
*
- * Interface is a way to access the peer (sender or receiver) via network.
+ * Interface is a way to access the node (sender or receiver) via network.
*
- * Each peer slot has multiple interfaces, one of each type. The user
- * interconnects peers by binding one of the first peer's interfaces to an URI
- * and then connecting the corresponding second peer's interface to that URI.
+ * Each node slot has multiple interfaces, one of each type. The user interconnects nodes by
+ * binding one of the first node's interfaces to an URI and then connecting the corresponding
+ * second node's interface to that URI.
*
* A URI is represented by {@link Endpoint} object.
*
- * The interface defines the type of the communication with the remote peer and
- * the set of protocols (URI schemes) that can be used with this particular
- * interface.
+ * The interface defines the type of the communication with the remote node and the set of
+ * protocols (URI schemes) that can be used with this particular interface.
*
- * {@link Interface#CONSOLIDATED} is an interface for high-level protocols which
- * automatically manage all necessary communication: transport streams, control
- * messages, parameter negotiation, etc. When a consolidated connection is
- * established, peers may automatically setup lower-level interfaces like {@link
- * Interface#AUDIO_SOURCE}, {@link Interface#AUDIO_REPAIR}, and {@link
- * Interface#AUDIO_CONTROL}.
+ * {@link Interface#CONSOLIDATED} is an interface for high-level protocols which automatically
+ * manage all necessary communication: transport streams, control messages, parameter
+ * negotiation, etc. When a consolidated connection is established, nodes may automatically setup
+ * lower-level interfaces like {@link Interface#AUDIO_SOURCE}, {@link Interface#AUDIO_REPAIR},
+ * and {@link Interface#AUDIO_CONTROL}.
*
- * {@link Interface#CONSOLIDATED} is mutually exclusive with lower-level
- * interfaces. In most cases, the user needs only {@link
- * Interface#CONSOLIDATED}. However, the lower-level interfaces may be useful if
- * an external signaling mechanism is used or for compatibility with third-party
+ * {@link Interface#CONSOLIDATED} is mutually exclusive with lower-level interfaces. In most
+ * cases, the user needs only {@link Interface#CONSOLIDATED}. However, the lower-level interfaces
+ * may be useful if an external signaling mechanism is used or for compatibility with third-party
* software.
*
- * {@link Interface#AUDIO_SOURCE} and {@link Interface#AUDIO_REPAIR} are
- * lower-level unidirectional transport-only interfaces. The first is used to
- * transmit audio stream, and the second is used to transmit redundant repair
- * stream, if FEC is enabled.
+ * {@link Interface#AUDIO_SOURCE} and {@link Interface#AUDIO_REPAIR} are lower-level
+ * unidirectional transport-only interfaces. The first is used to transmit audio stream, and the
+ * second is used to transmit redundant repair stream, if FEC is enabled.
*
- * {@link Interface#AUDIO_CONTROL} is a lower-level interface for control
- * streams. If you use {@link Interface#AUDIO_SOURCE} and {@link
- * Interface#AUDIO_REPAIR}, you usually also need to use {@link
- * Interface#AUDIO_CONTROL} to enable carrying additional non-transport
- * information.
+ * {@link Interface#AUDIO_CONTROL} is a lower-level interface for control streams. If you use
+ * {@link Interface#AUDIO_SOURCE} and {@link Interface#AUDIO_REPAIR}, you usually also need to
+ * use {@link Interface#AUDIO_CONTROL} to enable carrying additional non-transport information.
*/
public enum Interface {
/**
- * Interface that consolidates all types of streams (source, repair,
- * control).
+ * Interface that consolidates all types of streams (source, repair, control).
*
- * Allowed operations:
- * Allowed protocols:
- * Allowed operations:
- * Allowed protocols:
- * Allowed operations:
- * Allowed protocols:
- * Allowed operations:
- * Allowed protocols:
+ * Sender and receiver can have multiple slots ( {@link Slot} ), and each slot
+ * can be bound or connected to multiple interfaces ( {@link Interface} ).
+ *
+ * Each such interface has its own configuration, defined by this class.
+ *
+ * See {@link RocSender#configure()}, {@link RocReceiver#configure()}.
+ */
+@Getter
+@Builder(builderClassName = "Builder", toBuilder = true)
+@ToString
+@EqualsAndHashCode
+public class InterfaceConfig {
+
+ /**
+ * Outgoing IP address.
+ *
+ * If non-empty, explicitly identifies the OS network interface, by its IP address, from
+ * which to send outgoing packets. If NULL, the network interface is selected
+ * automatically by the OS, depending on the address of remote endpoint.
+ *
+ * For example, if eth0 has IP address "192.168.0.1", then setting outgoing address to
+ * "192.168.0.1" will force usage of eth0 interface.
+ *
+ * Setting it to {@code 0.0.0.0} (for IPv4) or to {@code ::} (for IPv6) gives the same
+ * effect as if it was NULL.
+ *
+ * By default, empty.
+ */
+ private String outgoingAddress;
+
+ /**
+ * Multicast group IP address.
+ *
+ * Multicast group should be set only when binding interface to an endpoint with
+ * multicast IP address. If present, it defines an IP address of the OS network interface
+ * on which to join the multicast group. If not present, no multicast group is joined.
+ *
+ * It's possible to receive multicast traffic from only those OS network interfaces, on
+ * which the process has joined the multicast group. When using multicast, the user
+ * should either set this field, or join multicast group manually using OS-specific API.
+ *
+ * It is allowed to set multicast group to {@code 0.0.0.0} (for IPv4) or to {@code ::}
+ * (for IPv6), to be able to receive multicast traffic from all available interfaces.
+ * However, this may not be desirable for security reasons.
+ *
+ * By default, empty.
+ */
+ private String multicastGroup;
+
+ /**
+ * Socket address reuse flag.
+ *
+ * When true (non-zero), SO_REUSEADDR is enabled for socket, regardless of socket type,
+ * unless binding to ephemeral port (when port is set to zero).
+ *
+ * When false (zero), SO_REUSEADDR is enabled for socket only if it has multicast type,
+ * unless binding to ephemeral port (when port is set to zero).
+ *
+ * For TCP-based protocols, SO_REUSEADDR allows immediate reuse of recently closed socket
+ * in TIME_WAIT state, which may be useful you want to be able to restart server quickly.
+ *
+ * For UDP-based protocols, SO_REUSEADDR allows multiple processes to bind to the same
+ * address, which may be useful if you're using socket activation mechanism.
+ *
+ * By default, false.
+ */
+ private boolean reuseAddress;
+
+ /**
+ * Construct builder for {@link InterfaceConfig}.
+ */
+ public static InterfaceConfig.Builder builder() {
+ return new InterfaceConfigValidator();
+ }
+}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/InterfaceConfigValidator.java b/src/main/java/org/rocstreaming/roctoolkit/InterfaceConfigValidator.java
new file mode 100644
index 00000000..0184a84c
--- /dev/null
+++ b/src/main/java/org/rocstreaming/roctoolkit/InterfaceConfigValidator.java
@@ -0,0 +1,13 @@
+package org.rocstreaming.roctoolkit;
+
+/**
+ * Adds validation to {@link InterfaceConfig} builder.
+ */
+class InterfaceConfigValidator extends InterfaceConfig.Builder {
+ @Override
+ public InterfaceConfig build() {
+ InterfaceConfig config = super.build();
+
+ return config;
+ }
+}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/MediaEncoding.java b/src/main/java/org/rocstreaming/roctoolkit/MediaEncoding.java
new file mode 100644
index 00000000..43a87715
--- /dev/null
+++ b/src/main/java/org/rocstreaming/roctoolkit/MediaEncoding.java
@@ -0,0 +1,57 @@
+// Code generated by bindgen.py from roc-streaming/bindgen
+// roc-toolkit git tag: v0.3.0, commit: 57b932b8
+
+package org.rocstreaming.roctoolkit;
+
+import java.time.Duration;
+import lombok.*;
+
+/**
+ * Media encoding.
+ *
+ * Defines format and parameters of samples encoded in frames or packets.
+ */
+@Getter
+@Builder(builderClassName = "Builder", toBuilder = true)
+@ToString
+@EqualsAndHashCode
+public class MediaEncoding {
+
+ /**
+ * Sample frequency.
+ *
+ * Defines number of samples per channel per second (e.g. 44100).
+ */
+ private int rate;
+
+ /**
+ * Sample format.
+ *
+ * Defines sample precision and encoding.
+ */
+ private Format format;
+
+ /**
+ * Channel layout.
+ *
+ * Defines number of channels and meaning of each channel.
+ */
+ private ChannelLayout channels;
+
+ /**
+ * Multi-track channel count.
+ *
+ * If {@code channels} is {@link ChannelLayout#MULTITRACK}, defines number of channels
+ * (which represent independent "tracks"). For other channel layouts should be zero.
+ *
+ * Should be in range [1; 1024].
+ */
+ private int tracks;
+
+ /**
+ * Construct builder for {@link MediaEncoding}.
+ */
+ public static MediaEncoding.Builder builder() {
+ return new MediaEncodingValidator();
+ }
+}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/MediaEncodingValidator.java b/src/main/java/org/rocstreaming/roctoolkit/MediaEncodingValidator.java
new file mode 100644
index 00000000..ff369bfe
--- /dev/null
+++ b/src/main/java/org/rocstreaming/roctoolkit/MediaEncodingValidator.java
@@ -0,0 +1,28 @@
+package org.rocstreaming.roctoolkit;
+
+/**
+ * Adds validation to {@link MediaEncoding} builder.
+ */
+class MediaEncodingValidator extends MediaEncoding.Builder {
+ @Override
+ public MediaEncoding build() {
+ MediaEncoding encoding = super.build();
+
+ Check.notNegative(encoding.getRate(), "MediaEncoding.rate");
+ Check.notNull(encoding.getFormat(), "MediaEncoding.format");
+ Check.notNull(encoding.getChannels(), "MediaEncoding.channels");
+ Check.notNegative(encoding.getTracks(), "MediaEncoding.tracks");
+
+ if (encoding.getChannels() == ChannelLayout.MULTITRACK) {
+ if (encoding.getTracks() < 1 || encoding.getTracks() > 1024) {
+ throw new IllegalArgumentException("Invalid MediaEncoding: when 'channels' is MULTITRACK, 'tracks' must be in range [1; 1024]");
+ }
+ } else {
+ if (encoding.getTracks() != 0) {
+ throw new IllegalArgumentException("Invalid MediaEncoding: when 'channels' isn't MULTITRACK, 'tracks' must be zero");
+ }
+ }
+
+ return encoding;
+ }
+}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/NativeObject.java b/src/main/java/org/rocstreaming/roctoolkit/NativeObject.java
index 09189810..b10c3414 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/NativeObject.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/NativeObject.java
@@ -1,12 +1,12 @@
package org.rocstreaming.roctoolkit;
/**
- * A
* Remove any phantom reachable {@link NativeObjectPhantomReference} from the
- * {@link NativeObjectCleaner#referenceQueue} associated with this
+ * Each packet encoding defines sample format, channel layout, and rate. Each packet encoding is
+ * caompatible with specific protocols.
*/
-public enum PacketEncoding {
+@EqualsAndHashCode
+public class PacketEncoding {
/**
- * PCM signed 16-bit.
+ * PCM signed 16-bit, 1 channel, 44100 rate.
*
- * "L16" encoding from RTP A/V Profile (RFC 3551). Uncompressed samples
- * coded as interleaved 16-bit signed big-endian integers in two's
+ * Represents 1-channel L16 stereo encoding from RTP A/V Profile (RFC 3551). Uses
+ * uncompressed samples coded as interleaved 16-bit signed big-endian integers in two's
* complement notation.
+ *
+ * Supported by protocols:
+ *
+ * Represents 2-channel L16 stereo encoding from RTP A/V Profile (RFC 3551). Uses
+ * uncompressed samples coded as interleaved 16-bit signed big-endian integers in two's
+ * complement notation.
+ *
+ * Supported by protocols:
+ *
- * Interfaces:
- * Transports:
- * Interfaces:
- * Transports:
- * Audio encodings:
- * FEC encodings:
- * Interfaces:
- * Transports:
- * Audio encodings:
- * FEC encodings:
- * Interfaces:
- * Transports:
- * FEC encodings:
- * Interfaces:
- * Transports:
- * Audio encodings:
- * FEC encodings:
- * Interfaces:
- * Transports:
- * FEC encodings:
- * Interfaces:
- * Transports:
- * Affects speed and quality. Some backends may be disabled at build time.
+ * Affects CPU usage, quality, and clock synchronization precision. Some backends may be disabled
+ * at build time.
*/
public enum ResamplerBackend {
/**
* Default backend.
*
- * Depends on what was enabled at build time.
+ * Selects {@link ResamplerBackend#BUILTIN} when using
+ * {@link ClockSyncProfile#RESPONSIVE}, or when SpeexDSP is disabled.
+ *
+ * Otherwise, selects {@link ResamplerBackend#SPEEX}.
*/
DEFAULT(0),
/**
- * Slow built-in resampler.
+ * CPU-intensive good-quality high-precision built-in resampler.
+ *
+ * This backend controls clock speed with very high precision, and hence is useful when
+ * latency or synchronization error should be very low.
+ *
+ * This backend has higher CPU usage, especially on high resampling quality and on CPUs
+ * with small L3 caches.
+ *
+ * The implementation is based on bandlimited interpolation algorithm.
+ *
+ * This backend is always available.
*
- * Always available.
+ * Recommended for {@link ClockSyncProfile#RESPONSIVE} and on good CPUs.
*/
BUILTIN(1),
/**
- * Fast good-quality resampler from SpeexDSP.
+ * Fast good-quality low-precision resampler based on SpeexDSP.
*
- * May be disabled at build time.
+ * This backend has low CPU usage even on high resampler quality and cheap CPUs.
+ *
+ * This backend controls clock speed with lower precision, and is not so good when
+ * latency or synchronization error should be very low.
+ *
+ * This backend is available only when SpeexDSP was enabled at build time.
+ *
+ * Recommended for {@link ClockSyncProfile#GRADUAL} and on cheap CPUs.
*/
SPEEX(2),
+
+ /**
+ * Fast medium-quality and medium-precision resampler combining SpeexDSP with decimation.
+ *
+ * This backend uses SpeexDSP for converting between base rates (e.g. 44100 vs 48000) and
+ * decimation/expansion (dropping or duplicating samples) for clock drift compensation.
+ *
+ * Typical decimation rate needed to compensate clock drift is below 0.5ms/second (20
+ * samples/second on 48Khz), which gives tolerable quality despite usage of decimation,
+ * especially for speech.
+ *
+ * When frame and packet sample rates are equal (e.g. both are 44100), only decimation
+ * stage is needed, and this becomes fastest possible backend working almost as fast as
+ * memcpy().
+ *
+ * When frame and packet rates are different, usage of this backend compared to
+ * {@link ResamplerBackend#SPEEX} allows to sacrify some quality, but somewhat improve
+ * scaling precision and CPU usage in return.
+ *
+ * This backend is available only when SpeexDSP was enabled at build time.
+ *
+ * Recommended when CPU resources are extremely limited.
+ */
+ SPEEXDEC(3),
;
final int value;
diff --git a/src/main/java/org/rocstreaming/roctoolkit/ResamplerProfile.java b/src/main/java/org/rocstreaming/roctoolkit/ResamplerProfile.java
index 1b188002..246f72f3 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/ResamplerProfile.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/ResamplerProfile.java
@@ -1,24 +1,15 @@
-// DO NOT EDIT! Code generated by generate_enums script from roc-toolkit
-// roc-toolkit git tag: v0.2.5-11-g14d642e9, commit: 14d642e9
+// Code generated by bindgen.py from roc-streaming/bindgen
+// roc-toolkit git tag: v0.3.0, commit: 57b932b8
package org.rocstreaming.roctoolkit;
/**
* Resampler profile.
*
- * Affects speed and quality. Each resampler backend treats profile in its own
- * way.
+ * Affects CPU usage and quality. Each resampler backend treats profile in its own way.
*/
public enum ResamplerProfile {
- /**
- * Do not perform resampling.
- *
- * Clock drift compensation will be disabled in this case. If in doubt, do
- * not disable resampling.
- */
- DISABLE(-1),
-
/**
* Default profile.
*
@@ -27,17 +18,17 @@ public enum ResamplerProfile {
DEFAULT(0),
/**
- * High quality, low speed.
+ * High quality, higher CPU usage.
*/
HIGH(1),
/**
- * Medium quality, medium speed.
+ * Medium quality, medium CPU usage.
*/
MEDIUM(2),
/**
- * Low quality, high speed.
+ * Low quality, lower CPU usage.
*/
LOW(3),
;
diff --git a/src/main/java/org/rocstreaming/roctoolkit/RocContext.java b/src/main/java/org/rocstreaming/roctoolkit/RocContext.java
index 051311c7..19313d64 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/RocContext.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/RocContext.java
@@ -6,52 +6,65 @@
/**
* Shared context.
*
- * Context contains memory pools and network worker threads, shared among objects attached
- * to the context. It is allowed both to create a separate context for every object, or
- * to create a single context shared between multiple objects.
+ * Context contains memory pools and network worker threads, shared among objects attached to the
+ * context. It is allowed both to create a separate context for every object, or to create a
+ * single context shared between multiple objects.
+ *
+ *
+ *
+ * A context is created using {@link RocContext()} and destroyed using
+ * {@link RocContext#close()}. Objects can be attached and detached to an opened context at any
+ * moment from any thread. However, the user should ensure that the context is not closed until
+ * there are no objects attached to the context.
+ *
+ *
+ *
- *
+ * {@code RocContext} class implements {@link AutoCloseable}, so if it is used in a
+ * try-with-resources statement, the object is closed automatically at the end of the statement.
+ *
*
- * @see RocSender
- * @see RocReceiver
+ * @see {@link RocSender}
+ * @see {@link RocReceiver}
*/
public class RocContext extends NativeObject {
private static final Logger LOGGER = Logger.getLogger(RocContext.class.getName());
- /**
- * Validate context constructor parameters and open a new context if validation is successful.
- *
- * @param config should point to an initialized config.
- *
- * @return the native roc context pointer.
- *
- * @throws IllegalArgumentException if the arguments are invalid.
- * @throws Exception if there are not enough resources.
- */
- private static long construct(RocContextConfig config) throws IllegalArgumentException, Exception {
- Check.notNull(config, "config");
- LOGGER.log(Level.FINE, "starting RocContext.open(), config={0}", new Object[]{config});
- long ptr = open(config);
- LOGGER.log(Level.FINE, "finished RocContext.open(), ptr={0}", new Object[]{toHex(ptr)});
- return ptr;
+ private static long construct(RocContextConfig config) throws RocException {
+ Check.notNull(config, "RocContextConfig");
+
+ try {
+ LOGGER.log(Level.FINE, "entering RocContext(), config={0}", config);
+
+ long ptr = nativeOpen(config);
+
+ LOGGER.log(Level.FINE, "leaving RocContext(), ptr={0}", toHex(ptr));
+ return ptr;
+ } catch (Exception exc) {
+ LOGGER.log(Level.SEVERE, "exception in RocContext(), exception={0}", exc);
+ throw exc;
+ }
}
- /**
- * Destruct native object
- */
- private static void destroy(long ptr) throws Exception {
- LOGGER.log(Level.FINE, "starting RocContext.close(), ptr={0}", new Object[]{toHex(ptr)});
- close(ptr);
- LOGGER.log(Level.FINE, "finished RocContext.close(), ptr={0}", new Object[]{toHex(ptr)});
+ private static void destroy(long ptr) {
+ try {
+ LOGGER.log(Level.FINE, "entering RocContext.close(), ptr={0}", toHex(ptr));
+
+ nativeClose(ptr);
+
+ LOGGER.log(Level.FINE, "leaving RocContext.close(), ptr={0}", toHex(ptr));
+ } catch (RuntimeException exc) {
+ LOGGER.log(Level.SEVERE, "exception in RocContext.close(), ptr={0}, exception={1}",
+ new Object[]{toHex(ptr), exc});
+ throw exc;
+ }
}
/**
@@ -59,10 +72,10 @@ private static void destroy(long ptr) throws Exception {
*
* Allocates and initializes a new context. May start some background threads.
*
- * @throws IllegalArgumentException if the arguments are invalid.
- * @throws Exception if there are not enough resources.
+ * @throws IllegalArgumentException if the arguments are invalid.
+ * @throws RocException if operation failed.
*/
- public RocContext() throws IllegalArgumentException, Exception {
+ public RocContext() throws RocException {
this(RocContextConfig.builder().build());
}
@@ -71,15 +84,57 @@ public RocContext() throws IllegalArgumentException, Exception {
*
* Allocates and initializes a new context. May start some background threads.
*
- * @param config should point to an initialized config.
+ * @param config should point to an initialized config.
*
- * @throws IllegalArgumentException if the arguments are invalid.
- * @throws Exception if there are not enough resources.
+ * @throws IllegalArgumentException if the arguments are invalid.
+ * @throws RocException if operation failed.
*/
- public RocContext(RocContextConfig config) throws IllegalArgumentException, Exception {
+ public RocContext(RocContextConfig config) throws RocException {
super(construct(config), null, RocContext::destroy);
}
- private static native long open(RocContextConfig config) throws IllegalArgumentException, Exception;
- private static native void close(long nativePtr) throws Exception;
+ /**
+ * Register custom encoding.
+ *
+ * Registers {@code encoding} with given {@code encoding_id}. Registered encodings
+ * complement built-in encodings defined by {@link PacketEncoding} enum. Whenever you
+ * need to specify packet encoding, you can use both built-in and registered encodings.
+ *
+ * On sender, you should register custom encoding and set to {@code packetEncoding} field
+ * of {@link RocSenderConfig}, if you need to force specific encoding of packets, but
+ * built-in set of encodings is not enough.
+ *
+ * On receiver, you should register custom encoding with same id and specification, if
+ * you did so on sender, and you're not using any signaling protocol (like RTSP) that is
+ * capable of automatic exchange of encoding information.
+ *
+ * In case of RTP, encoding id is mapped directly to payload type field (PT).
+ *
+ * @param encodingId is numeric encoding identifier in range {@code [1; 127]}.
+ * @param encoding is encoding specification to be associated with this id.
+ *
+ * @throws IllegalArgumentException if the arguments are invalid.
+ * @throws RocException if operation failed.
+ */
+ public void registerEncoding(int encodingId, MediaEncoding encoding) throws RocException {
+ Check.inRange(encodingId, 1, 127, "encodingId");
+ Check.notNull(encoding, "MediaEncoding");
+
+ try {
+ LOGGER.log(Level.FINE, "entering RocContext.registerEncoding(), ptr={0}, encodingId={1}, encoding={2}",
+ new Object[]{toHex(getPtr()), encodingId, encoding});
+
+ nativeRegisterEncoding(getPtr(), encodingId, encoding);
+
+ LOGGER.log(Level.FINE, "leaving RocContext.registerEncoding(), ptr={0}", toHex(getPtr()));
+ } catch (Exception exc) {
+ LOGGER.log(Level.SEVERE, "exception in RocContext.registerEncoding(), ptr={0}, exception={1}",
+ new Object[]{toHex(getPtr()), exc});
+ throw exc;
+ }
+ }
+
+ private static native long nativeOpen(RocContextConfig config) throws RocException;
+ private static native void nativeClose(long contextPtr);
+ private static native void nativeRegisterEncoding(long contextPtr, int encodingId, MediaEncoding encoding) throws RocException;
}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/RocContextConfig.java b/src/main/java/org/rocstreaming/roctoolkit/RocContextConfig.java
index 89831ee9..6d2f38cc 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/RocContextConfig.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/RocContextConfig.java
@@ -1,5 +1,9 @@
+// Code generated by bindgen.py from roc-streaming/bindgen
+// roc-toolkit git tag: v0.3.0, commit: 57b932b8
+
package org.rocstreaming.roctoolkit;
+import java.time.Duration;
import lombok.*;
/**
@@ -17,23 +21,24 @@ public class RocContextConfig {
/**
* Maximum size in bytes of a network packet.
- * Defines the amount of bytes allocated per network packet.
- * Sender and receiver won't handle packets larger than this.
- * If zero or unset, default value is used.
- * Should not be negative.
+ *
+ * Defines the amount of bytes allocated per network packet. Sender and receiver won't
+ * handle packets larger than this. If zero, default value is used.
*/
private int maxPacketSize;
/**
* Maximum size in bytes of an audio frame.
- * Defines the amount of bytes allocated per intermediate internal
- * frame in the pipeline. Does not limit the size of the frames
- * provided by user.
- * If zero or unset, default value is used.
- * Should not be negative.
+ *
+ * Defines the amount of bytes allocated per intermediate internal frame in the pipeline.
+ * Does not limit the size of the frames provided by user. If zero, default value is
+ * used.
*/
private int maxFrameSize;
+ /**
+ * Construct builder for {@link RocContextConfig}.
+ */
public static RocContextConfig.Builder builder() {
return new RocContextConfigValidator();
}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/RocContextConfigValidator.java b/src/main/java/org/rocstreaming/roctoolkit/RocContextConfigValidator.java
index 0b920919..0a66ca42 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/RocContextConfigValidator.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/RocContextConfigValidator.java
@@ -1,14 +1,16 @@
package org.rocstreaming.roctoolkit;
/**
- * A
- * Messages with log levels higher than param level will be dropped.
- * By default the log level is set to {@link RocLogLevel#ERROR ERROR}.
- *
- * @param level maximum log level.
- */
- private native static void setLevel(RocLogLevel level);
-
- private native static void setHandler(RocLogHandler handler);
-
- private RocLogger() {
- }
+ private native static void nativeSetLevel(RocLogLevel level);
+ private native static void nativeSetHandler(RocLogHandler handler);
}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/RocReceiver.java b/src/main/java/org/rocstreaming/roctoolkit/RocReceiver.java
index a9cd797a..bba96345 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/RocReceiver.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/RocReceiver.java
@@ -1,160 +1,182 @@
package org.rocstreaming.roctoolkit;
-import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
- * Receiver peer.
+ * Receiver node.
*
- * Receiver gets the network packets from multiple senders, decodes audio streams
- * from them, mixes multiple streams into a single stream, and returns it to the user.
+ * Receiver gets the network packets from multiple senders, decodes audio streams from them,
+ * mixes multiple streams into a single stream, and returns it to the user.
+ *
*
*
- * Receiver is automatically attached to a context when opened and detached from it when
- * closed. The user should not close the context until the receiver is closed.
+ * Receiver is automatically attached to a context when opened and detached from it when closed.
+ * The user should not close the context until the receiver is closed.
*
- * Receiver work consists of two parts: packet reception and stream decoding. The
- * decoding part is performed in the receiver itself, and the reception part is
- * performed in the context network worker threads.
+ * Receiver work consists of two parts: packet reception and stream decoding. The decoding part
+ * is performed in the receiver itself, and the reception part is performed in the context
+ * network worker threads.
+ *
*
- *
*
* Receiver has one or multiple slots, which may be independently bound or connected.
- * Slots may be used to bind receiver to multiple addresses. Slots are numbered from
- * zero and are created automatically. In simple cases just use {@link Slot#DEFAULT}.
+ * Slots may be used to bind receiver to multiple addresses. Slots are numbered from zero and are
+ * created automatically. In simple cases just use {@code ROC_SLOT_DEFAULT}.
*
- * Each slot has its own set of interfaces, one per each type defined in {@link Interface}.
- * The interface defines the type of the communication with the remote peer
+ * Each slot has its own set of interfaces, one per each type defined in
+ * {@link Interface}. The interface defines the type of the communication with the remote node
* and the set of the protocols supported by it.
*
* Supported actions with the interface:
+ *
*
* Supported interface configurations:
+ *
*
+ * Slots can be removed using {@link RocReceiver#unlink()}. Removing a slot also removes all its
+ * interfaces and terminates all associated connections.
+ *
+ * Slots can be added and removed at any time on fly and from any thread. It is safe to do it
+ * from another thread concurrently with reading frames. Operations with slots won't block
+ * concurrent reads.
+ *
*
*
- * If {@link Interface#CONSOLIDATED} is used, it automatically creates all necessary
- * transport interfaces and the user should not bother about them.
+ * If {@link Interface#CONSOLIDATED} is used, it automatically creates all necessary transport
+ * interfaces and the user should not bother about them.
*
* Otherwise, the user should manually configure {@link Interface#AUDIO_SOURCE} and
* {@link Interface#AUDIO_REPAIR} interfaces:
+ *
*
- * The protocols for the two interfaces should correspond to each other and to the FEC
- * scheme. For example, if {@link FecEncoding#RS8M} is used, the protocols should be
+ * The protocols for the two interfaces should correspond to each other and to the FEC scheme.
+ * For example, if {@link FecEncoding#RS8M} is used, the protocols should be
* {@link Protocol#RTP_RS8M_SOURCE} and {@link Protocol#RS8M_REPAIR}.
*
+ *
*
- * A session is identified by the sender address. A session may contain multiple packet
- * streams sent to different receiver ports. If the sender employs FEC, the session will
- * contain source and repair packet streams. Otherwise, the session will contain a single
- * source packet stream.
+ * Receiver creates a session object for every sender connected to it. Sessions can appear and
+ * disappear at any time. Multiple sessions can be active at the same time.
+ *
+ * A session is identified by the sender address. A session may contain multiple packet streams
+ * sent to different receiver ports. If the sender employs FEC, the session will contain source
+ * and repair packet streams. Otherwise, the session will contain a single source packet stream.
*
- * A session is created automatically on the reception of the first packet from a new
- * address and destroyed when there are no packets during a timeout. A session is also
- * destroyed on other events like a large latency underrun or overrun or broken playback,
- * but if the sender continues to send packets, it will be created again shortly.
+ * A session is created automatically on the reception of the first packet from a new address and
+ * destroyed when there are no packets during a timeout. A session is also destroyed on other
+ * events like a large latency underrun or overrun or broken playback, but if the sender
+ * continues to send packets, it will be created again shortly.
+ *
*
*
- * The output stream continues no matter how much active sessions there are at the moment.
- * In particular, if there are no sessions, the receiver produces a stream with all zeros.
+ * Receiver mixes audio streams from all currently active sessions into a single output stream.
+ *
+ * The output stream continues no matter how much active sessions there are at the moment. In
+ * particular, if there are no sessions, the receiver produces a stream with all zeros.
*
- * Sessions can be added and removed from the output stream at any time, probably in the
- * middle of a frame.
+ * Sessions can be added and removed from the output stream at any time, probably in the middle
+ * of a frame.
+ *
*
*
- * Receiver compensates these differences by adjusting the rate of every session stream to
- * the rate of the receiver output stream using a per-session resampler. The frequencies
- * factor between the sender and the receiver clocks is calculated dynamically for every
- * session based on the session incoming packet queue size.
+ * Every session may have a different sample rate. And even if nominally all of them are of the
+ * same rate, device frequencies usually differ by a few tens of Hertz.
*
- * Resampling is a quite time-consuming operation. The user can choose between completely
- * disabling resampling (at the cost of occasional underruns or overruns) or several
- * resampler profiles providing different compromises between CPU consumption and quality.
+ * Receiver compensates these differences by adjusting the rate of every session stream to the
+ * rate of the receiver output stream using a per-session resampler. The frequencies factor
+ * between the sender and the receiver clocks is calculated dynamically for every session based
+ * on the session incoming packet queue size.
+ *
+ * Resampling is a quite time-consuming operation. The user can choose between several resampler
+ * profiles providing different compromises between CPU consumption and quality.
+ *
*
*
- * This mode is useful when the user passes samples to a non-realtime destination, e.g. to an audio file.
- *
- * This mode is useful when the user passes samples to a realtime destination with its
- * own clock, e.g. to an audio device. Internal clock should not be used in this case
- * because the audio device and the CPU might have slightly different clocks, and the
- * difference will eventually lead to an underrun or an overrun.
- *
- * Can be used concurrently
+ * Can be used concurrently.
+ *
*
*
* Allocates and initializes a new receiver, and attaches it to the context.
*
- * @param context should point to an opened context.
- * @param config should point to an initialized config.
- * @throws IllegalArgumentException if the arguments are invalid.
- * @throws Exception if an error occurred when creating the receiver.
+ * @param context should point to an opened context.
+ * @param config should point to an initialized config.
+ *
+ * @throws IllegalArgumentException if the arguments are invalid.
+ * @throws RocException if operation failed.
*/
- public RocReceiver(RocContext context, RocReceiverConfig config) throws IllegalArgumentException, Exception {
+ public RocReceiver(RocContext context, RocReceiverConfig config) throws RocException {
super(construct(context, config), context, ptr -> destroy(ptr, context));
}
/**
- * Set receiver interface multicast group.
- *
- * Optional.
- *
- * Multicast group should be set only when binding receiver interface to an endpoint with
- * multicast IP address. If present, it defines an IP address of the OS network interface
- * on which to join the multicast group. If not present, no multicast group is joined.
- *
- * It's possible to receive multicast traffic from only those OS network interfaces, on
- * which the process has joined the multicast group. When using multicast, the user should
- * either call this function, or join multicast group manually using OS-specific API.
- *
- * It is allowed to set multicast group to `0.0.0.0` (for IPv4) or to `::` (for IPv6),
- * to be able to receive multicast traffic from all available interfaces. However, this
- * may not be desirable for security reasons.
+ * Set receiver interface configuration.
*
- * Each slot's interface can have only one multicast group. The function should be called
- * before calling {@link RocReceiver#bind bind()} for the interface. It should not be called when
- * calling {@link RocReceiver#connect connect()} for the interface.
+ * Updates configuration of specified interface of specified slot. If
+ * called, the call should be done before calling {@link RocReceiver#bind()}
+ * or roc_receiver_connect() for the same interface.
*
* Automatically initializes slot with given index if it's used first time.
+ *
+ * If an error happens during configure, the whole slot is disabled and
+ * marked broken. The slot index remains reserved. The user is responsible
+ * for removing the slot using {@link RocReceiver#unlink()}, after which
+ * slot index can be reused.
+ *
+ * @param slot specifies the receiver slot.
+ * @param iface specifies the receiver interface.
+ * @param config specifies settings for the specified interface.
*
- * @param slot specifies the receiver slot
- * @param iface specifies the receiver interface
- * @param ip should be IPv4 or IPv6 address
- * @throws IllegalArgumentException if the arguments are invalid.
+ * @throws IllegalArgumentException if the arguments are invalid.
+ * @throws RocException if operation failed.
*/
- public void setMulticastGroup(Slot slot, Interface iface, String ip) throws IllegalArgumentException, Exception {
- Check.notNull(slot, "slot");
- Check.notNull(iface, "iface");
- Check.notEmpty(ip, "ip");
+ public void configure(Slot slot, Interface iface, InterfaceConfig config) throws RocException {
+ Check.notNull(slot, "Slot");
+ Check.notNull(iface, "Interface");
+ Check.notNull(config, "InterfaceConfig");
+
+ try {
+ LOGGER.log(Level.FINE, "entering RocReceiver.configure(), ptr={0}, slot={1}, iface={2}, config={3}",
+ new Object[]{toHex(getPtr()), slot, iface, config});
+
+ nativeConfigure(getPtr(), slot.getValue(), iface.value, config);
- LOGGER.log(Level.FINE, "starting RocReceiver.setMulticastGroup(), ptr={0}, slot={1}, iface={2}, ip={3}",
- new Object[]{toHex(getPtr()), slot, iface, ip});
- setMulticastGroup(getPtr(), slot.getValue(), iface.value, ip);
- LOGGER.log(Level.FINE, "finished RocReceiver.setMulticastGroup(), ptr={0}", new Object[]{toHex(getPtr())});
+ LOGGER.log(Level.FINE, "leaving RocReceiver.configure(), ptr={0}", toHex(getPtr()));
+ } catch (Exception exc) {
+ LOGGER.log(Level.SEVERE, "exception in RocReceiver.configure(), ptr={0}, exception={1}",
+ new Object[]{toHex(getPtr()), exc});
+ throw exc;
+ }
}
/**
* Bind the receiver interface to a local endpoint.
*
- * Checks that the endpoint is valid and supported by the interface, allocates
- * a new ingoing port, and binds it to the local endpoint.
+ * Checks that the endpoint is valid and supported by the interface,
+ * allocates a new ingoing port, and binds it to the local endpoint.
*
- * Each slot's interface can be bound or connected only once.
- * May be called multiple times for different slots or interfaces.
+ * Each slot's interface can be bound or connected only once. May be called
+ * multiple times for different slots or interfaces.
*
* Automatically initializes slot with given index if it's used first time.
*
- * If
+ * If {@code endpoint} has explicitly set zero port, the receiver is bound
+ * to a randomly chosen ephemeral port. If the function succeeds, the actual
+ * port to which the receiver was bound is written back to {@code endpoint}.
+ *
+ * @param slot specifies the receiver slot.
+ * @param iface specifies the receiver interface.
+ * @param endpoint specifies the receiver endpoint.
*
- * @param slot specifies the receiver slot
- * @param iface specifies the receiver interface
- * @param endpoint specifies the receiver endpoint
- * @throws IllegalArgumentException if the arguments are invalid.
- * @throws IOException if the address can't be bound or if there are not enough resources.
+ * @throws IllegalArgumentException if the arguments are invalid.
+ * @throws RocException if operation failed.
*/
- public void bind(Slot slot, Interface iface, Endpoint endpoint) throws IllegalArgumentException, IOException {
- Check.notNull(slot, "slot");
- Check.notNull(iface, "iface");
- Check.notNull(endpoint, "endpoint");
+ public void bind(Slot slot, Interface iface, Endpoint endpoint) throws RocException {
+ Check.notNull(slot, "Slot");
+ Check.notNull(iface, "Interface");
+ Check.notNull(endpoint, "Endpoint");
+
+ try {
+ LOGGER.log(Level.FINE, "entering RocReceiver.bind(), ptr={0}, slot={1}, iface={2}, endpoint={3}",
+ new Object[]{toHex(getPtr()), slot, iface, endpoint});
- LOGGER.log(Level.FINE, "starting RocReceiver.bind(), ptr={0}, slot={1}, iface={2}, endpoint={3}",
- new Object[]{toHex(getPtr()), slot, iface, endpoint});
- bind(getPtr(), slot.getValue(), iface.value, endpoint);
- LOGGER.log(Level.FINE, "finished RocReceiver.bind(), ptr={0}, endpoint={1}", new Object[]{toHex(getPtr()), endpoint});
+ nativeBind(getPtr(), slot.getValue(), iface.value, endpoint);
+
+ LOGGER.log(Level.FINE, "leaving RocReceiver.bind(), ptr={0}", toHex(getPtr()));
+ } catch (Exception exc) {
+ LOGGER.log(Level.SEVERE, "exception in RocReceiver.bind(), ptr={0}, exception={1}",
+ new Object[]{toHex(getPtr()), exc});
+ throw exc;
+ }
}
- public void connect(Endpoint endpoint) {
- throw new RuntimeException("connect not implemented");
+ /**
+ * Delete receiver slot.
+ *
+ * Disconnects, unbinds, and removes all slot interfaces and removes the
+ * slot. All associated connections to remote nodes are properly terminated.
+ *
+ * After unlinking the slot, it can be re-created again by re-using slot
+ * index.
+ *
+ * @param slot specifies the receiver slot to delete.
+ *
+ * @throws IllegalArgumentException if the arguments are invalid.
+ * @throws RocException if operation failed.
+ */
+ public void unlink(Slot slot) throws RocException {
+ Check.notNull(slot, "Slot");
+
+ try {
+ LOGGER.log(Level.FINE, "entering RocReceiver.unlink(), ptr={0}, slot={1}",
+ new Object[]{toHex(getPtr()), slot});
+
+ nativeUnlink(getPtr(), slot.getValue());
+
+ LOGGER.log(Level.FINE, "leaving RocReceiver.unlink(), ptr={0}", toHex(getPtr()));
+ } catch (Exception exc) {
+ LOGGER.log(Level.SEVERE, "exception in RocReceiver.unlink(), ptr={0}, exception={1}",
+ new Object[]{toHex(getPtr()), exc});
+ throw exc;
+ }
}
/**
* Read samples from the receiver.
*
- * Reads network packets received on bound ports, routes packets to sessions, repairs lost
- * packets, decodes samples, resamples and mixes them, and finally stores samples into the
- * provided frame.
+ * Reads retrieved network packets, decodes packets, routes packets to
+ * sessions, repairs losses, extracts samples, adjusts sample rate and
+ * channel layout, compensates clock drift, mixes samples from all sessions,
+ * and finally stores samples into the provided frame.
*
- * If {@link ClockSource#INTERNAL} is used, the function blocks until it's time to decode the
- * samples according to the configured sample rate.
+ * If {@link ClockSource#INTERNAL} is used, the function blocks until it's
+ * time to decode the samples according to the configured sample rate.
*
- * Until the receiver is connected to at least one sender, it produces silence.
- * If the receiver is connected to multiple senders, it mixes their streams into one.
+ * Until the receiver is connected to at least one sender, it produces
+ * silence. If the receiver is connected to multiple senders, it mixes their
+ * streams into one.
+ *
+ * @param samples should point to an initialized {@code float} array which will be
+ * filled with samples.
*
- * @param samples should point to an initialized
+ * Frame encoding defines sample format, channel layout, and sample rate in local frames
+ * returned by receiver to user. Should be set (zero value is invalid).
*/
- private int frameSampleRate;
+ private MediaEncoding frameEncoding;
/**
- * The channel set in the frames returned to the user.
- * Should be set to a non-null value.
+ * Clock source.
+ *
+ * Defines whether read operation will be blocking or non-blocking. If zero,
+ * {@link ClockSource#EXTERNAL} is used.
*/
- private ChannelSet frameChannels;
+ private ClockSource clockSource;
/**
- * The sample encoding in the frames returned to the user.
- * Should be set to a non-null value.
+ * Clock synchronization backend.
+ *
+ * Defines how sender and receiver clocks are synchronized. If zero, default value is
+ * used.
*/
- private FrameEncoding frameEncoding;
+ private ClockSyncBackend clockSyncBackend;
/**
- * Clock source to use.
- * Defines whether read operation will be blocking or non-blocking.
- * If null or unset, default value is used.
+ * Clock synchronization profile.
+ *
+ * Defines what latency and network jitter are tolerated. If zero, default value is used.
*/
- private ClockSource clockSource;
+ private ClockSyncProfile clockSyncProfile;
/**
- * Resampler backend to use.
- * If null or unset, default value is used.
+ * Resampler backend.
+ *
+ * Affects CPU usage, quality, and clock synchronization precision. If zero, default
+ * value is used.
*/
private ResamplerBackend resamplerBackend;
/**
- * Resampler profile to use.
- * If null or unset, default value is used.
- * If resampling is enabled, the receiver employs resampler for two purposes:
- *
+ * Affects CPU usage and quality. If zero, default value is used.
*/
private ResamplerProfile resamplerProfile;
/**
* Target latency, in nanoseconds.
- * The session will not start playing until it accumulates the
- * requested latency.
- * Then, if resampler is enabled, the session will adjust its clock
- * to keep actual latency as close as possible to the target latency.
- * If zero or unset, default value is used.
- * Should not be negative.
+ *
+ * The session will not start playing until it accumulates the requested latency. Then,
+ * if clock synchronization is enabled, the session will adjust its clock to keep actual
+ * latency as close as possible to the target latency. If zero, default value is used.
*/
private Duration targetLatency;
/**
- * Maximum delta between current and target latency, in nanoseconds.
- * If current latency becomes larger than the target latency plus
- * this value, the session is terminated.
- * If zero or unset, default value is used.
- * Should not be negative.
+ * Maximum allowed delta between current and target latency, in nanoseconds.
+ *
+ * If session latency differs from the target latency by more than given value, the
+ * session is terminated (it can then automatically restart). Receiver itself is not
+ * terminated; if there are no sessions, it will produce zeros. If zero, default value is
+ * used.
*/
- private Duration maxLatencyOverrun;
-
- /**
- * Maximum delta between target and current latency, in nanoseconds.
- * If current latency becomes smaller than the target latency minus
- * this value, the session is terminated.
- * May be larger than the target latency because current latency may
- * be negative, which means that the playback run ahead of the last
- * packet received from network.
- * If zero or unset, default value is used.
- * Should not be negative.
- */
- private Duration maxLatencyUnderrun;
+ private Duration latencyTolerance;
/**
* Timeout for the lack of playback, in nanoseconds.
- * If there is no playback during this period, the session is terminated.
- * This mechanism allows to detect dead, hanging, or broken clients
- * generating invalid packets.
- * If zero or unset, default value is used.
- * If negative, the timeout is disabled.
+ *
+ * If there is no playback during this period, the session is terminated (it can then
+ * automatically restart). Receiver itself is not terminated; if there are no sessions,
+ * it will produce zeros. This mechanism allows to detect dead, hanging, or incompatible
+ * clients that generate unparseable packets. If zero, default value is used. If
+ * negative, the timeout is disabled.
*/
private Duration noPlaybackTimeout;
/**
- * Timeout for broken playback, in nanoseconds.
- * If there the playback is considered broken during this period,
- * the session is terminated. The playback is broken if there is
- * a breakage detected at every
+ * If there is constant stuttering during this period, the session is terminated (it can
+ * then automatically restart). Receiver itself is not terminated; if there are no
+ * sessions, it will produce zeros. This mechanism allows to detect situations when
+ * playback continues but there are frequent glitches, for example because there is a
+ * high ratio of late packets. If zero, default value is used. If negative, the timeout
+ * is disabled.
*/
- private Duration brokenPlaybackTimeout;
+ private Duration choppyPlaybackTimeout;
/**
- * Breakage detection window, in nanoseconds.
- * If zero or unset, default value is used.
- * Should not be negative.
+ * Construct builder for {@link RocReceiverConfig}.
*/
- private Duration breakageDetectionWindow;
-
public static RocReceiverConfig.Builder builder() {
return new RocReceiverConfigValidator();
}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/RocReceiverConfigValidator.java b/src/main/java/org/rocstreaming/roctoolkit/RocReceiverConfigValidator.java
index ab58ba9d..71a66c06 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/RocReceiverConfigValidator.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/RocReceiverConfigValidator.java
@@ -1,19 +1,17 @@
package org.rocstreaming.roctoolkit;
/**
- * A
- * Sender gets an audio stream from the user, encodes it into network packets, and
- * transmits them to a remote receiver.
+ * Sender gets an audio stream from the user, encodes it into network packets, and transmits them
+ * to a remote receiver.
+ *
*
*
- * Sender is automatically attached to a context when opened and detached from it when
- * closed. The user should not close the context until the sender is closed.
+ * Sender is automatically attached to a context when opened and detached from it when closed.
+ * The user should not close the context until the sender is closed.
*
- * Sender work consists of two parts: stream encoding and packet transmission. The
- * encoding part is performed in the sender itself, and the transmission part is
- * performed in the context network worker threads.
+ * Sender work consists of two parts: stream encoding and packet transmission. The encoding part
+ * is performed in the sender itself, and the transmission part is performed in the context
+ * network worker threads.
*
- *
- *
- *
- * Sender has one or multiple slots, which may be independently bound or connected.
- * Slots may be used to connect sender to multiple receivers. Slots are numbered from
- * zero and are created automatically. In simple cases just use {@link Slot#DEFAULT}.
+ * Sender has one or multiple slots, which may be independently bound or connected. Slots
+ * may be used to connect sender to multiple receivers. Slots are numbered from zero and are
+ * created automatically. In simple cases just use {@code ROC_SLOT_DEFAULT}.
*
- * Each slot has its own set of interfaces, one per each type defined in {@link Interface}.
- * The interface defines the type of the communication with the remote peer
+ * Each slot has its own set of interfaces, one per each type defined in
+ * {@link Interface}. The interface defines the type of the communication with the remote node
* and the set of the protocols supported by it.
*
* Supported actions with the interface:
+ *
*
* Supported interface configurations:
+ *
*
+ * Slots can be removed using {@link RocSender#unlink()}. Removing a slot also removes all its
+ * interfaces and terminates all associated connections.
+ *
+ * Slots can be added and removed at any time on fly and from any thread. It is safe to do it
+ * from another thread concurrently with writing frames. Operations with slots won't block
+ * concurrent writes.
+ *
*
*
- * If {@link Interface#CONSOLIDATED} is used, it automatically creates all necessary
- * transport interfaces and the user should not bother about them.
+ * If {@link Interface#CONSOLIDATED} is used, it automatically creates all necessary transport
+ * interfaces and the user should not bother about them.
*
* Otherwise, the user should manually configure {@link Interface#AUDIO_SOURCE} and
* {@link Interface#AUDIO_REPAIR} interfaces:
+ *
*
- * The protocols for the two interfaces should correspond to each other and to the FEC
- * scheme. For example, if {@link FecEncoding#RS8M} is used, the protocols should be
+ * The protocols for the two interfaces should correspond to each other and to the FEC scheme.
+ * For example, if {@link FecEncoding#RS8M} is used, the protocols should be
* {@link Protocol#RTP_RS8M_SOURCE} and {@link Protocol#RS8M_REPAIR}.
*
+ *
*
* If the sample rate of the user frames and the sample rate of the network packets are
* different, the sender employs resampler to convert one rate to another.
*
- * Resampling is a quite time-consuming operation. The user can choose between completely
- * disabling resampling (and so use the same rate for frames and packets) or several
- * resampler profiles providing different compromises between CPU consumption and quality.
+ * Resampling is a quite time-consuming operation. The user can choose between several resampler
+ * profiles providing different compromises between CPU consumption and quality.
+ *
*
*
- * Sender should encode samples at a constant rate that is configured when the sender
- * is created. There are two ways to accomplish this:
+ * Sender should encode samples at a constant rate that is configured when the sender is created.
+ * There are two ways to accomplish this:
*
- *
- * This mode is useful when the user gets samples from a non-realtime source, e.g.
- * from an audio file.
- *
- * This mode is useful when the user gets samples from a realtime source with its own
- * clock, e.g. from an audio device. Internal clock should not be used in this case
- * because the audio device and the CPU might have slightly different clocks, and the
- * difference will eventually lead to an underrun or an overrun.
- *
- * Can be used concurrently
+ * Can be used concurrently.
+ *
*
*
- * Optional. Should be used only when connecting an interface to a remote endpoint.
+ * Updates configuration of specified interface of specified slot. If
+ * called, the call should be done before calling roc_sender_bind() or
+ * {@link RocSender#connect()} for the same interface.
*
- * If set, explicitly defines the IP address of the OS network interface from which to
- * send the outgoing packets. If not set, the outgoing interface is selected automatically
- * by the OS, depending on the remote endpoint address.
+ * Automatically initializes slot with given index if it's used first time.
*
- * It is allowed to set outgoing address to `0.0.0.0` (for IPv4) or to `::` (for IPv6),
- * to achieve the same behavior as if it wasn't set, i.e. to let the OS to select the
- * outgoing interface automatically.
+ * If an error happens during configure, the whole slot is disabled and
+ * marked broken. The slot index remains reserved. The user is responsible
+ * for removing the slot using {@link RocSender#unlink()}, after which slot
+ * index can be reused.
+ *
+ * @param slot slot specifies the sender slot.
+ * @param iface iface specifies the sender interface.
+ * @param config settings for the specified interface.
+ *
+ * @throws IllegalArgumentException if the arguments are invalid.
+ * @throws RocException if operation failed.
+ */
+ public void configure(Slot slot, Interface iface, InterfaceConfig config) throws RocException {
+ Check.notNull(slot, "Slot");
+ Check.notNull(iface, "Interface");
+ Check.notNull(config, "InterfaceConfig");
+
+ try {
+ LOGGER.log(Level.FINE, "entering RocSender.configure(), ptr={0}, slot={1}, iface={2}, config={3}",
+ new Object[]{toHex(getPtr()), slot, iface, config});
+
+ nativeConfigure(getPtr(), slot.getValue(), iface.value, config);
+
+ LOGGER.log(Level.FINE, "leaving RocSender.configure(), ptr={0}", toHex(getPtr()));
+ } catch (Exception exc) {
+ LOGGER.log(Level.SEVERE, "exception in RocSender.configure(), ptr={0}, exception={1}",
+ new Object[]{toHex(getPtr()), exc});
+ throw exc;
+ }
+ }
+
+ /**
+ * Connect the sender interface to a remote receiver endpoint.
*
- * By default, the outgoing address is not set.
+ * Checks that the endpoint is valid and supported by the interface,
+ * allocates a new outgoing port, and connects it to the remote endpoint.
*
- * Each slot's interface can have only one outgoing address. The function should be called
- * before calling {@link RocSender#connect connect()} for this slot and interface. It should not be
- * called when calling {@link RocSender#bind bind()} for the interface.
+ * Each slot's interface can be bound or connected only once. May be called
+ * multiple times for different slots or interfaces.
*
* Automatically initializes slot with given index if it's used first time.
*
- * **Parameters**
+ * If an error happens during connect, the whole slot is disabled and marked
+ * broken. The slot index remains reserved. The user is responsible for
+ * removing the slot using {@link RocSender#unlink()}, after which slot
+ * index can be reused.
*
- * @param slot specifies the sender slot
- * @param iface specifies the sender interface
- * @param ip should be IPv4 or IPv6 address
- * @throws Exception if an error occurred
+ * @param slot slot specifies the sender slot.
+ * @param iface iface specifies the sender interface.
+ * @param endpoint endpoint specifies the receiver endpoint.
+ *
+ * @throws IllegalArgumentException if the arguments are invalid.
+ * @throws RocException if operation failed.
*/
- public void setOutgoingAddress(Slot slot, Interface iface, String ip) throws Exception {
- Check.notNull(slot, "slot");
- Check.notNull(iface, "iface");
- Check.notEmpty(ip, "ip");
+ public void connect(Slot slot, Interface iface, Endpoint endpoint) throws RocException {
+ Check.notNull(slot, "Slot");
+ Check.notNull(iface, "Interface");
+ Check.notNull(endpoint, "Endpoint");
- LOGGER.log(Level.FINE, "starting RocSender.setOutgoingAddress(), ptr={0}, slot={1}, iface={2}, ip={3}",
- new Object[]{toHex(getPtr()), slot, iface, ip});
- setOutgoingAddress(getPtr(), slot.getValue(), iface.value, ip);
- LOGGER.log(Level.FINE, "finished RocSender.setOutgoingAddress(), ptr={0}", new Object[]{toHex(getPtr())});
- }
+ try {
+ LOGGER.log(Level.FINE, "entering RocSender.connect(), ptr={0}, slot={1}, iface={2}, endpoint={3}",
+ new Object[]{toHex(getPtr()), slot, iface, endpoint});
- public void bind(Endpoint endpoint) throws IllegalArgumentException, IOException {
- throw new RuntimeException("bind not implemented");
+ nativeConnect(getPtr(), slot.getValue(), iface.value, endpoint);
+
+ LOGGER.log(Level.FINE, "leaving RocSender.connect(), ptr={0}", toHex(getPtr()));
+ } catch (Exception exc) {
+ LOGGER.log(Level.SEVERE, "exception in RocSender.connect(), ptr={0}, exception={1}",
+ new Object[]{toHex(getPtr()), exc});
+ throw exc;
+ }
}
/**
- * Connect the sender interface to a remote receiver endpoint.
+ * Delete sender slot.
*
- * Checks that the endpoint is valid and supported by the interface, allocates
- * a new outgoing port, and connects it to the remote endpoint.
+ * Disconnects, unbinds, and removes all slot interfaces and removes the
+ * slot. All associated connections to remote nodes are properly terminated.
*
- * Each slot's interface can be bound or connected only once.
- * May be called multiple times for different slots or interfaces.
- *
- * Automatically initializes slot with given index if it's used first time.
+ * After unlinking the slot, it can be re-created again by re-using slot
+ * index.
+ *
+ * @param slot specifies the sender slot to delete.
*
- * @param slot slot specifies the sender slot
- * @param iface iface specifies the sender interface
- * @param endpoint endpoint specifies the receiver endpoint
- * @throws IllegalArgumentException if the arguments are invalid.
- * @throws IOException if was error during connect
+ * @throws IllegalArgumentException if the arguments are invalid.
+ * @throws RocException if operation failed.
*/
- public void connect(Slot slot, Interface iface, Endpoint endpoint) throws IllegalArgumentException,
- IOException {
- Check.notNull(slot, "slot");
- Check.notNull(iface, "iface");
- Check.notNull(endpoint, "endpoint");
+ public void unlink(Slot slot) throws RocException {
+ Check.notNull(slot, "Slot");
- LOGGER.log(Level.FINE, "starting RocSender.connect(), ptr={0}, slot={1}, iface={2}, endpoint={3}",
- new Object[]{toHex(getPtr()), slot, iface, endpoint});
- connect(getPtr(), slot.getValue(), iface.value, endpoint);
- LOGGER.log(Level.FINE, "finished RocSender.connect(), ptr={0}", new Object[]{toHex(getPtr())});
+ try {
+ LOGGER.log(Level.FINE, "entering RocSender.unlink(), ptr={0}, slot={1}",
+ new Object[]{toHex(getPtr()), slot});
+
+ nativeUnlink(getPtr(), slot.getValue());
+
+ LOGGER.log(Level.FINE, "leaving RocSender.unlink(), ptr={0}", toHex(getPtr()));
+ } catch (Exception exc) {
+ LOGGER.log(Level.SEVERE, "exception in RocSender.unlink(), ptr={0}, exception={1}",
+ new Object[]{toHex(getPtr()), exc});
+ throw exc;
+ }
}
/**
* Encode samples to packets and transmit them to the receiver.
*
- * Encodes samples to packets and enqueues them for transmission by the network worker
- * thread of the context.
+ * Encodes samples to packets and enqueues them for transmission by the
+ * network worker thread of the context.
*
- * If {@link ClockSource#INTERNAL} is used, the function blocks until it's time to transmit the
- * samples according to the configured sample rate. The function returns after encoding
- * and enqueuing the packets, without waiting when the packets are actually transmitted.
+ * If {@link ClockSource#INTERNAL} is used, the function blocks until it's
+ * time to transmit the samples according to the configured sample rate. The
+ * function returns after encoding and enqueuing the packets, without
+ * waiting when the packets are actually transmitted.
*
- * Until the sender is connected to at least one receiver, the stream is just dropped.
- * If the sender is connected to multiple receivers, the stream is duplicated to
- * each of them.
+ * Until the sender is connected to at least one receiver, the stream is
+ * just dropped. If the sender is connected to multiple receivers, the
+ * stream is duplicated to each of them.
+ *
+ * @param samples array of samples to send.
*
- * @param samples array of samples to send.
- * @throws IllegalArgumentException if the arguments are invalid.
- * @throws IOException if the sender if there are not enough resources.
+ * @throws IllegalArgumentException if the arguments are invalid.
+ * @throws RocException if operation failed.
*/
- public void write(float[] samples) throws IllegalArgumentException, IOException {
+ public void write(float[] samples) throws RocException {
Check.notNull(samples, "samples");
- writeFloats(getPtr(), samples);
- }
-
- private static native long open(long contextPtr, RocSenderConfig config) throws IllegalArgumentException, Exception;
- private native void setOutgoingAddress(long senderPtr, int slot, int iface, String ip) throws Exception;
+ nativeWriteFloats(getPtr(), samples);
+ }
- private native void connect(long senderPtr, int slot, int iface, Endpoint endpoint) throws IOException;
+ private static native long nativeOpen(long contextPtr, RocSenderConfig config) throws RocException;
+ private static native void nativeClose(long senderPtr);
- private native void writeFloats(long senderPtr, float[] samples) throws IOException;
+ private native void nativeConfigure(long senderPtr, int slot, int iface, InterfaceConfig config) throws RocException;
+ private native void nativeConnect(long senderPtr, int slot, int iface, Endpoint endpoint) throws RocException;
+ private native void nativeUnlink(long senderPtr, int slot) throws RocException;
- private static native void close(long senderPtr) throws IOException;
+ private native void nativeWriteFloats(long senderPtr, float[] samples) throws RocException;
}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/RocSenderConfig.java b/src/main/java/org/rocstreaming/roctoolkit/RocSenderConfig.java
index 7b55d274..5d6bb0a1 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/RocSenderConfig.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/RocSenderConfig.java
@@ -1,3 +1,6 @@
+// Code generated by bindgen.py from roc-streaming/bindgen
+// roc-toolkit git tag: v0.3.0, commit: 57b932b8
+
package org.rocstreaming.roctoolkit;
import java.time.Duration;
@@ -17,111 +20,119 @@
public class RocSenderConfig {
/**
- * The rate of the samples in the frames returned to the user.
- * Number of samples per channel per second.
- * Should be set to a positive value.
+ * The encoding used in frames passed to sender.
+ *
+ * Frame encoding defines sample format, channel layout, and sample rate in local frames
+ * created by user and passed to sender. Should be set (zero value is invalid).
*/
- private int frameSampleRate;
+ private MediaEncoding frameEncoding;
/**
- * The channel set in the frames returned to the user.
- * Should be set to a non-null value.
+ * The encoding used for packets produced by sender.
+ *
+ * Packet encoding defines sample format, channel layout, and sample rate in network
+ * packets. If packet encoding differs from frame encoding, conversion is performed
+ * automatically.
+ *
+ * If zero, sender selects packet encoding automatically based on {@code frameEncoding}.
+ * This automatic selection matches only encodings that have exact same sample rate and
+ * channel layout, and hence don't require conversions. If you need conversions, you
+ * should set packet encoding explicitly.
+ *
+ * If you want to force specific packet encoding, and built-in set of encodings is not
+ * enough, you can use {@link RocContext#registerEncoding()} to register custom encoding,
+ * set {@code packetEncoding} to registered identifier. If you use signaling protocol
+ * like RTSP, it's enough to register in just on sender; otherwise, you need to do the
+ * same on receiver as well.
*/
- private ChannelSet frameChannels;
-
- /**
- * The sample encoding in the frames returned to the user.
- * Should be set to a non-null value.
- */
- private FrameEncoding frameEncoding;
+ private PacketEncoding packetEncoding;
/**
- * The rate of the samples in the packets generated by sender.
- * Number of samples per channel per second.
- * If zero or unset, default value is used.
- * Should not be negative.
+ * The length of the packets produced by sender, in nanoseconds.
+ *
+ * Number of nanoseconds encoded per packet. The samples written to the sender are
+ * buffered until the full packet is accumulated or the sender is flushed or closed.
+ * Larger number reduces packet overhead but also increases latency. If zero, default
+ * value is used.
*/
- private int packetSampleRate;
+ private Duration packetLength;
/**
- * The channel set in the packets generated by sender.
- * If null or unset, default value is used.
+ * Enable packet interleaving.
+ *
+ * If non-zero, the sender shuffles packets before sending them. This may increase
+ * robustness but also increases latency.
*/
- private ChannelSet packetChannels;
+ private int packetInterleaving;
/**
- * The sample encoding in the packets generated by sender.
- * If null or unset, default value is used.
+ * FEC encoding to use.
+ *
+ * If non-zero, the sender employs a FEC encoding to generate redundant packets which may
+ * be used on receiver to restore lost packets. This requires both sender and receiver to
+ * use two separate source and repair endpoints.
*/
- private PacketEncoding packetEncoding;
+ private FecEncoding fecEncoding;
/**
- * The length of the packets produced by sender, in nanoseconds.
- * Number of nanoseconds encoded per packet.
- * The samples written to the sender are buffered until the full
- * packet is accumulated or the sender is flushed or closed.
- * Larger number reduces packet overhead but also increases latency.
- * If zero or unset, default value is used.
- * Should not be negative.
+ * Number of source packets per FEC block.
+ *
+ * Used if some FEC encoding is selected.
+ *
+ * Sender divides stream into blocks of N source (media) packets, and adds M repair
+ * (redundancy) packets to each block, where N is {@code fecBlockSourcePackets} and M is
+ * {@code fecBlockRepairPackets}.
+ *
+ * Larger number of source packets in block increases robustness (repair ratio), but also
+ * increases latency.
+ *
+ * If zero, default value is used.
*/
- private Duration packetLength;
+ private int fecBlockSourcePackets;
/**
- * Enable packet interleaving.
- * If non-zero, the sender shuffles packets before sending them. This
- * may increase robustness but also increases latency.
+ * Number of repair packets per FEC block.
+ *
+ * Used if some FEC encoding is selected.
+ *
+ * Sender divides stream into blocks of N source (media) packets, and adds M repair
+ * (redundancy) packets to each block, where N is {@code fecBlockSourcePackets} and M is
+ * {@code fecBlockRepairPackets}.
+ *
+ * Larger number of repair packets in block increases robustness (repair ratio), but also
+ * increases traffic. Number of repair packets usually should be 1/2 or 2/3 of the number
+ * of source packets.
+ *
+ * If zero, default value is used.
*/
- private int packetInterleaving;
+ private int fecBlockRepairPackets;
/**
* Clock source to use.
- * Defines whether write operation will be blocking or non-blocking.
- * If null or unset, default value is used.
+ *
+ * Defines whether write operation will be blocking or non-blocking. If zero, default
+ * value is used ( {@link ClockSource#EXTERNAL} ).
*/
private ClockSource clockSource;
/**
* Resampler backend to use.
- * If null or unset, default value is used.
+ *
+ * If zero, default value is used.
*/
private ResamplerBackend resamplerBackend;
/**
* Resampler profile to use.
- * If null or unset, default value is used.
- * If resampling is enabled, the sender employs resampler if the frame sample rate
- * differs from the packet sample rate.
+ *
+ * If non-zero, the sender employs resampler if the frame sample rate differs from the
+ * packet sample rate.
*/
private ResamplerProfile resamplerProfile;
/**
- * FEC encoding to use.
- * If null or unset, default value is used.
- * If FEC is enabled, the sender employs a FEC codec to generate redundant
- * packets which may be used on receiver to restore lost packets.
- * This requires both sender and receiver to use two separate source
- * and repair ports.
- */
- private FecEncoding fecEncoding;
-
- /**
- * Number of source packets per FEC block.
- * Used if some FEC encoding is selected.
- * Larger number increases robustness but also increases latency.
- * If zero or unset, default value is used.
- * Should not be negative.
- */
- private int fecBlockSourcePackets;
-
- /**
- * Number of repair packets per FEC block.
- * Used if some FEC encoding is selected.
- * Larger number increases robustness but also increases traffic.
- * If zero or unset, default value is used.
- * Should not be negative.
+ * Construct builder for {@link RocSenderConfig}.
*/
- private int fecBlockRepairPackets;
-
public static RocSenderConfig.Builder builder() {
return new RocSenderConfigValidator();
}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/RocSenderConfigValidator.java b/src/main/java/org/rocstreaming/roctoolkit/RocSenderConfigValidator.java
index dcfec5ed..676ed346 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/RocSenderConfigValidator.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/RocSenderConfigValidator.java
@@ -1,19 +1,18 @@
package org.rocstreaming.roctoolkit;
/**
- * A
+ *
+ *
+ *
+ */
+ NIQ(2),
+ ;
+
+ final int value;
+
+ ClockSyncBackend(int value) {
+ this.value = value;
+ }
+}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/ClockSyncProfile.java b/src/main/java/org/rocstreaming/roctoolkit/ClockSyncProfile.java
new file mode 100644
index 00000000..69fa3351
--- /dev/null
+++ b/src/main/java/org/rocstreaming/roctoolkit/ClockSyncProfile.java
@@ -0,0 +1,67 @@
+// Code generated by bindgen.py from roc-streaming/bindgen
+// roc-toolkit git tag: v0.3.0, commit: 57b932b8
+
+package org.rocstreaming.roctoolkit;
+
+/**
+ * Clock synchronization profile.
+ *
+ *
+ *
+ *
+ */
+ RESPONSIVE(1),
+
+ /**
+ * Gradual clock adjustment.
+ *
+ *
+ *
+ *
+ */
+ GRADUAL(2),
+ ;
+
+ final int value;
+
+ ClockSyncProfile(int value) {
+ this.value = value;
+ }
+}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/Destructor.java b/src/main/java/org/rocstreaming/roctoolkit/Destructor.java
index 3f15073d..809b138b 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/Destructor.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/Destructor.java
@@ -13,14 +13,14 @@ interface Destructor {
* user or asynchronously by {@link NativeObjectCleaner}.
*
* static and not as an
+ * Note: It's important that this method is declared {@code static} and not as an
* instance method for avoiding object resurrection.
* Endpoint URI
*
- *
*
- *
* Field invalidation
* Thread-safety
+ * If some field is attempted to be set to an invalid value (for example, an invalid port
+ * number), this specific field is marked as invalid until it is successfully set to some valid
+ * value.
* Thread safety
+ *
+ *
+ *
+ *
*/
DISABLE(-1),
@@ -25,18 +37,38 @@ public enum FecEncoding {
/**
* Reed-Solomon FEC encoding (RFC 6865) with m=8.
*
+ *
+ *
+ *
*/
RS8M(1),
/**
* LDPC-Staircase FEC encoding (RFC 6816).
*
+ *
+ *
+ *
*/
LDPC_STAIRCASE(2),
;
diff --git a/src/main/java/org/rocstreaming/roctoolkit/Format.java b/src/main/java/org/rocstreaming/roctoolkit/Format.java
new file mode 100644
index 00000000..e50926e4
--- /dev/null
+++ b/src/main/java/org/rocstreaming/roctoolkit/Format.java
@@ -0,0 +1,27 @@
+// Code generated by bindgen.py from roc-streaming/bindgen
+// roc-toolkit git tag: v0.3.0, commit: 57b932b8
+
+package org.rocstreaming.roctoolkit;
+
+/**
+ * Sample format.
+ *
- *
+ *
*
- *
+ *
*/
CONSOLIDATED(1),
@@ -62,15 +57,17 @@ public enum Interface {
/**
* Interface for audio stream source data.
*
- *
+ *
*
- *
+ *
*/
AUDIO_SOURCE(11),
@@ -78,14 +75,16 @@ public enum Interface {
/**
* Interface for audio stream repair data.
*
- *
+ *
*
- *
+ *
*/
AUDIO_REPAIR(12),
@@ -93,13 +92,15 @@ public enum Interface {
/**
* Interface for audio control messages.
*
- *
+ *
*
- *
+ *
*/
AUDIO_CONTROL(13),
diff --git a/src/main/java/org/rocstreaming/roctoolkit/InterfaceConfig.java b/src/main/java/org/rocstreaming/roctoolkit/InterfaceConfig.java
new file mode 100644
index 00000000..d486c144
--- /dev/null
+++ b/src/main/java/org/rocstreaming/roctoolkit/InterfaceConfig.java
@@ -0,0 +1,86 @@
+// Code generated by bindgen.py from roc-streaming/bindgen
+// roc-toolkit git tag: v0.3.0, commit: 57b932b8
+
+package org.rocstreaming.roctoolkit;
+
+import java.time.Duration;
+import lombok.*;
+
+/**
+ * Interface configuration.
+ * NativeObject represents an underlying native roc object.
+ * A {@code NativeObject} represents an underlying native roc object.
*/
class NativeObject implements AutoCloseable {
/**
- * NativeObject finalizer thread.
+ * {@code NativeObject} finalizer thread.
*/
private final static NativeObjectCleaner NATIVE_OBJECT_CLEANER = NativeObjectCleaner.getInstance();
@@ -21,21 +21,21 @@ class NativeObject implements AutoCloseable {
}
/**
- * Construct a NativeObject.
+ * Construct a {@code NativeObject}.
*
* @param ptr native pointer to a roc native object
* @param dependsOn dependency for finalization ordering
- * @param destructor destructor method for closing NativeObject.
+ * @param destructor destructor method for closing {@code NativeObject}.
*/
protected NativeObject(long ptr, NativeObject dependsOn, Destructor destructor) {
this.resource = NATIVE_OBJECT_CLEANER.register(this, ptr, dependsOn, destructor);
}
/**
- * Get NativeObject native pointer.
+ * Get {@code NativeObject} native pointer.
*
* @return the native roc object pointer associated to this
- * NativeObject.
+ * {@code NativeObject}.
*/
long getPtr() {
return this.resource.getPtr();
@@ -44,10 +44,11 @@ long getPtr() {
/**
* Close the native object and unregister it from the {@link NativeObjectCleaner}.
*
- * @throws Exception if the underlying roc native object cannot be closed.
+ * @throws IllegalStateException if the underlying roc native object cannot be closed because
+ * it still has opened {@link NativeObject} dependencies.
*/
@Override
- public void close() throws Exception {
+ public void close() {
resource.close();
resource.clear();
NATIVE_OBJECT_CLEANER.unregister(resource);
diff --git a/src/main/java/org/rocstreaming/roctoolkit/NativeObjectCleaner.java b/src/main/java/org/rocstreaming/roctoolkit/NativeObjectCleaner.java
index 291e3655..72a517a6 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/NativeObjectCleaner.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/NativeObjectCleaner.java
@@ -28,13 +28,13 @@ class NativeObjectCleaner extends Thread {
/**
* Set to keep phantom references to prevent being garbage collected,
- * otherwise reference will be collected by GC and won't be queued to {@link NativeObjectCleaner#referenceQueue}
- * when related {@link NativeObject} collected by GC
+ * otherwise reference will be collected by GC and won't be queued to
+ * {@link NativeObjectCleaner#referenceQueue} when related {@link NativeObject} collected by GC
*/
private final SetNativeObjectCleaner.
+ * Create a new {@code NativeObjectCleaner}.
*/
private NativeObjectCleaner() {
super("RocNativeObjectCleaner");
@@ -43,16 +43,16 @@ private NativeObjectCleaner() {
}
/**
- * Get NativeObjectCleaner instance.
+ * Get {@code NativeObjectCleaner} instance.
*
- * @return the NativeObjectCleaner singleton instance.
+ * @return the {@code NativeObjectCleaner} singleton instance.
*/
static NativeObjectCleaner getInstance() {
return instance;
}
/**
- * Register a {@link NativeObject} in NativeObjectCleaner.
+ * Register a {@link NativeObject} in {@code NativeObjectCleaner}.
*
* @param nativeObj {@link NativeObject} to add.
* @param ptr Underlying roc object native pointer.
@@ -68,7 +68,7 @@ NativeObjectPhantomReference register(NativeObject nativeObj, long ptr, NativeOb
}
/**
- * Remove a reference from NativeObjectCleaner.
+ * Remove a reference from {@code NativeObjectCleaner}.
*
* @param reference the {@link NativeObjectPhantomReference} to unregister.
*/
@@ -78,11 +78,11 @@ void unregister(NativeObjectPhantomReference reference) {
}
/**
- * Entrypoint method of NativeObjectCleaner.
+ * Entrypoint method of {@code NativeObjectCleaner}.
* NativeObjectCleaner
- * and close it.
+ * {@link NativeObjectCleaner#referenceQueue} associated with this {@code NativeObjectCleaner}
+ * and {@code close()} it.
*/
@Override
public void run() {
diff --git a/src/main/java/org/rocstreaming/roctoolkit/NativeObjectPhantomReference.java b/src/main/java/org/rocstreaming/roctoolkit/NativeObjectPhantomReference.java
index 02296242..2be8f26d 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/NativeObjectPhantomReference.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/NativeObjectPhantomReference.java
@@ -5,9 +5,10 @@
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * NativeObjectPhantomReference is associated with a {@link NativeObject} and owns its entire lifetime;
+ * {@code NativeObjectPhantomReference} is associated with a {@link NativeObject} and owns its
+ * entire lifetime.
*
- * A NativeObjectPhantomReference contains necessary data for closing the native object
+ * A {@code NativeObjectPhantomReference} contains necessary data for closing the native object
* after it becomes phantom reachable.
*/
class NativeObjectPhantomReference extends PhantomReferenceNativeObjectPhantomReference.
+ * Construct a new {@code NativeObjectPhantomReference}.
*
* @param referent {@link NativeObject} associated.
* @param queue Reference queue containing phantom reachable native objects.
* @param ptr Underlying roc object native pointer.
- * @param dependsOn Dependency for finalization ordering. Keep strong reference to prevent it from being collected by GC
+ * @param dependsOn Dependency for finalization ordering. Keep strong reference to prevent
+ * it from being collected by GC
* @param destructor Destructor method.
*/
NativeObjectPhantomReference(NativeObject referent, ReferenceQueue super NativeObject> queue, long ptr, NativeObject dependsOn, Destructor destructor) {
@@ -53,7 +56,7 @@ class NativeObjectPhantomReference extends PhantomReferenceNativeObjectPhantomReference.
+ * @return the native roc object pointer associated to this {@code NativeObjectPhantomReference}.
*/
long getPtr() {
return ptr;
@@ -61,17 +64,19 @@ long getPtr() {
/**
* Close the native object.
+ *
+ * @throws IllegalStateException if the {@link NativeObject} cannot be closed because
+ * it still has opened {@link NativeObject} dependencies.
*/
@Override
- public synchronized void close() throws Exception {
+ public synchronized void close() {
if (isOpen) {
destructor.close(ptr);
// destructor.close(ptr) could throw exception e.g. if someone tried to close context while
// sender/receiver still opened.
- // In such case NativeObjectCleaner try to close it one more time after NativeObject
- // will be collected by GC
+ // In such case NativeObjectCleaner will try to close it one more time after NativeObject
+ // is collected by GC.
isOpen = false;
}
}
-
}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/PacketEncoding.java b/src/main/java/org/rocstreaming/roctoolkit/PacketEncoding.java
index e6a5773e..d456f7c5 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/PacketEncoding.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/PacketEncoding.java
@@ -1,26 +1,64 @@
-// DO NOT EDIT! Code generated by generate_enums script from roc-toolkit
-// roc-toolkit git tag: v0.2.5-11-g14d642e9, commit: 14d642e9
+// Code generated by bindgen.py from roc-streaming/bindgen
+// roc-toolkit git tag: v0.3.0, commit: 57b932b8
package org.rocstreaming.roctoolkit;
+import lombok.EqualsAndHashCode;
+
/**
* Packet encoding.
+ *
+ *
*/
- AVP_L16(2),
- ;
+ static PacketEncoding AVP_L16_MONO = new PacketEncoding(11);
+
+ /**
+ * PCM signed 16-bit, 2 channels, 44100 rate.
+ *
+ *
+ */
+ static PacketEncoding AVP_L16_STEREO = new PacketEncoding(10);
final int value;
- PacketEncoding(int value) {
+ public PacketEncoding(int value) {
this.value = value;
}
+
+ public int getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return "PacketEncoding(" + value + ")";
+ }
+
}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/Protocol.java b/src/main/java/org/rocstreaming/roctoolkit/Protocol.java
index bd11f400..bdacacf5 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/Protocol.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/Protocol.java
@@ -1,5 +1,5 @@
-// DO NOT EDIT! Code generated by generate_enums script from roc-toolkit
-// roc-toolkit git tag: v0.2.5-11-g14d642e9, commit: 14d642e9
+// Code generated by bindgen.py from roc-streaming/bindgen
+// roc-toolkit git tag: v0.3.0, commit: 57b932b8
package org.rocstreaming.roctoolkit;
@@ -13,13 +13,15 @@ public enum Protocol {
/**
* RTSP 1.0 (RFC 2326) or RTSP 2.0 (RFC 7826).
*
- *
+ *
*
- *
+ *
*/
RTSP(10),
@@ -27,42 +29,51 @@ public enum Protocol {
/**
* RTP over UDP (RFC 3550).
*
- *
+ *
*
- *
+ *
*
- *
+ *
*
- *
+ *
*/
RTP(20),
/**
- * RTP source packet (RFC 3550) + FECFRAME Reed-Solomon footer (RFC 6865)
- * with m=8.
+ * RTP source packet (RFC 3550) + FECFRAME Reed-Solomon footer (RFC 6865) with m=8.
*
- *
+ *
*
- *
+ *
*
- *
+ *
*
- *
+ *
*/
RTP_RS8M_SOURCE(30),
@@ -70,16 +81,19 @@ public enum Protocol {
/**
* FEC repair packet + FECFRAME Reed-Solomon header (RFC 6865) with m=8.
*
- *
+ *
*
- *
+ *
*
- *
+ *
*/
RS8M_REPAIR(31),
@@ -87,20 +101,24 @@ public enum Protocol {
/**
* RTP source packet (RFC 3550) + FECFRAME LDPC-Staircase footer (RFC 6816).
*
- *
+ *
*
- *
+ *
*
- *
+ *
*
- *
+ *
*/
RTP_LDPC_SOURCE(32),
@@ -108,16 +126,19 @@ public enum Protocol {
/**
* FEC repair packet + FECFRAME LDPC-Staircase header (RFC 6816).
*
- *
+ *
*
- *
+ *
*
- *
+ *
*/
LDPC_REPAIR(33),
@@ -125,12 +146,14 @@ public enum Protocol {
/**
* RTCP over UDP (RFC 3550).
*
- *
+ *
*
- *
+ *
*/
RTCP(70),
diff --git a/src/main/java/org/rocstreaming/roctoolkit/ProtocolUtils.java b/src/main/java/org/rocstreaming/roctoolkit/ProtocolUtils.java
deleted file mode 100644
index 372627a1..00000000
--- a/src/main/java/org/rocstreaming/roctoolkit/ProtocolUtils.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.rocstreaming.roctoolkit;
-
-import lombok.NoArgsConstructor;
-
-@SuppressWarnings("unused") // used by JNI
-@NoArgsConstructor
-class ProtocolUtils {
-
- private static Protocol getByValue(int value) {
- for (Protocol protocol : Protocol.values()) {
- if (value == protocol.value) {
- return protocol;
- }
- }
- return null;
- }
-}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/ResamplerBackend.java b/src/main/java/org/rocstreaming/roctoolkit/ResamplerBackend.java
index ba8f8d06..140ffe72 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/ResamplerBackend.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/ResamplerBackend.java
@@ -1,35 +1,80 @@
-// DO NOT EDIT! Code generated by generate_enums script from roc-toolkit
-// roc-toolkit git tag: v0.2.5-11-g14d642e9, commit: 14d642e9
+// Code generated by bindgen.py from roc-streaming/bindgen
+// roc-toolkit git tag: v0.3.0, commit: 57b932b8
package org.rocstreaming.roctoolkit;
/**
* Resampler backend.
* Life cycle
+ * Thread safety
* Lifecycle
- * A context is created using {@link #RocContext() RocContext()} or
- * {@link #RocContext(RocContextConfig) RocContext(RocContextConfig)} and destroyed using
- * {@link #close() close()}. RocContext class implements
- * {@link AutoCloseable AutoCloseable} so if it is used in a try-with-resources
- * statement the object is closed automatically at the end of the statement.
- * Objects can be attached and detached to an opened context at any moment from
- * any thread. However, the user should ensure that the context is not closed
- * until there are no objects attached to the context.
+ * Can be used concurrently.
+ *
+ *
+ * Auto closing
+ * RocContextConfigValidator adds validation to RocContextConfig builder.
+ * Adds validation to {@link RocContextConfig} builder.
*/
class RocContextConfigValidator extends RocContextConfig.Builder {
@Override
public RocContextConfig build() {
RocContextConfig config = super.build();
- Check.notNegative(config.getMaxPacketSize(), "maxPacketSize");
- Check.notNegative(config.getMaxFrameSize(), "maxFrameSize");
+
+ Check.notNegative(config.getMaxPacketSize(), "RocContextConfig.maxPacketSize");
+ Check.notNegative(config.getMaxFrameSize(), "RocContextConfig.maxFrameSize");
+
return config;
}
}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/RocException.java b/src/main/java/org/rocstreaming/roctoolkit/RocException.java
new file mode 100644
index 00000000..ac5e0e10
--- /dev/null
+++ b/src/main/java/org/rocstreaming/roctoolkit/RocException.java
@@ -0,0 +1,10 @@
+package org.rocstreaming.roctoolkit;
+
+/**
+ * Base class for all roctoolkit checked exceptions.
+ */
+public class RocException extends Exception {
+ public RocException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/RocLogLevel.java b/src/main/java/org/rocstreaming/roctoolkit/RocLogLevel.java
index b475311d..d1221aa6 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/RocLogLevel.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/RocLogLevel.java
@@ -5,7 +5,7 @@
*
* @see RocLogger#setLevel(RocLogLevel)
*/
-public enum RocLogLevel {
+enum RocLogLevel {
/**
* No messages.
diff --git a/src/main/java/org/rocstreaming/roctoolkit/RocLogger.java b/src/main/java/org/rocstreaming/roctoolkit/RocLogger.java
index c62a4641..1c66b4b1 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/RocLogger.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/RocLogger.java
@@ -7,7 +7,7 @@ class RocLogger {
static final Logger LOGGER = Logger.getLogger(RocLogger.class.getName());
- private static final RocLogHandler DEFAULT_HANDLER = (level, component, message) -> {
+ private static final RocLogHandler HANDLER = (level, component, message) -> {
Level julLevel = mapLogLevel(level);
if (LOGGER.isLoggable(julLevel)) {
LOGGER.logp(julLevel, component, "", message);
@@ -16,39 +16,40 @@ class RocLogger {
static {
RocLibrary.loadLibrary();
- setLevel(RocLogLevel.DEBUG); // set debug level and rely on jul to filter messages.
- setHandler(DEFAULT_HANDLER);
- // Jvm could be terminated before roclib, so we need to clear callback to avoid crash
- Runtime.getRuntime().addShutdownHook(new Thread(() -> setHandler(null)));
+ // Set debug level and rely on jul to filter messages.
+ nativeSetLevel(RocLogLevel.DEBUG);
+ // Register callback for libroc log messages.
+ nativeSetHandler(HANDLER);
+ // JVM could be terminated before libroc, so we need to unregister Java callback
+ // from libroc before shutting down, to avoid crash.
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ nativeSetLevel(RocLogLevel.NONE);
+ nativeSetHandler(null);
+ }));
+ }
+
+ private RocLogger() {
}
private static Level mapLogLevel(RocLogLevel level) {
switch (level) {
case NONE:
return Level.OFF;
+ case ERROR:
+ return Level.SEVERE;
case INFO:
return Level.INFO;
case DEBUG:
return Level.FINE;
case TRACE:
- return Level.FINER;
+ return Level.FINEST;
default:
- return Level.SEVERE;
+ break;
}
+ // Can't happen.
+ return Level.FINE;
}
- /**
- * Set maximum log level.
- * Context
* Lifecycle
+ * Life cycle
+ *
- *
*
+ *
* RocReceiver
- * class implements {@link AutoCloseable AutoCloseable} so if it is used in a try-with-resources
- * statement the object is closed automatically at the end of the statement.Slots, interfaces, and endpoints
*
- *
*
- *
+ * FEC scheme
*
- *
* Sessions
- * Receiver creates a session object for every sender connected to it. Sessions can appear
- * and disappear at any time. Multiple sessions can be active at the same time.
* Mixing
- * Receiver mixes audio streams from all currently active sessions into a single output
- * stream.
* Sample rate
- * Every session may have a different sample rate. And even if nominally all of them are
- * of the same rate, device frequencies usually differ by a few tens of Hertz.
* Clock source
- * Receiver should decode samples at a constant rate that is configured when the receiver
- * is created. There are two ways to accomplish this:
- *
- *
+ *
*
- * Thread-safety
+ *
+ * Thread safety
* Example
*
* {@code
- * RocReceiverConfig config = RocReceiverConfig.builder()
- * .frameSampleRate(SAMPLE_RATE)
- * .frameChannels(ChannelSet.STEREO)
- * .frameEncoding(FrameEncoding.PCM_FLOAT)
- * .build();
+ * RocReceiverConfig receiverConfig = RocReceiverConfig.builder()
+ * .frameEncoding(
+ * MediaEncoding.builder()
+ * .rate(44100)
+ * .format(Format.PCM_FLOAT32)
+ * .channels(ChannelLayout.STEREO)
+ * .build()
+ * )
+ * .clockSource(ClockSource.INTERNAL)
+ .build();
* try (
* RocContext context = new RocContext();
- * RocReceiver receiver = new RocReceiver(context, config);
+ * RocReceiver receiver = new RocReceiver(context, receiverConfig);
* ) {
* receiver.bind(Slot.DEFAULT, Interface.AUDIO_SOURCE, new Endpoint("rtp+rs8m://0.0.0.0:0"));
* receiver.bind(Slot.DEFAULT, Interface.AUDIO_REPAIR, new Endpoint("rs8m://0.0.0.0:0"));
@@ -164,6 +186,7 @@
* }
*
*
+ *
* @see RocContext
* @see RocReceiverConfig
* @see java.lang.AutoCloseable
@@ -172,36 +195,36 @@ public class RocReceiver extends NativeObject {
private static final Logger LOGGER = Logger.getLogger(RocReceiver.class.getName());
- /**
- * Validate receiver constructor parameters and open a new receiver if validation is successful.
- *
- * @param context should point to an opened context.
- * @param config should point to an initialized config.
- * @return the native roc receiver pointer.
- * @throws IllegalArgumentException if the arguments are invalid.
- * @throws Exception if an error occurred when creating the receiver.
- */
- private static long construct(RocContext context, RocReceiverConfig config) throws IllegalArgumentException, Exception {
- Check.notNull(context, "context");
- Check.notNull(config, "config");
+ private static long construct(RocContext context, RocReceiverConfig config) throws RocException {
+ Check.notNull(context, "RocContext");
+ Check.notNull(config, "RocReceiverConfig");
+
+ try {
+ LOGGER.log(Level.FINE, "entering RocReceiver(), contextPtr={0}, config={1}",
+ new Object[]{toHex(context.getPtr()), config});
- LOGGER.log(Level.FINE, "starting RocReceiver.open(), context ptr={0}, config={1}",
- new Object[]{toHex(context.getPtr()), config});
- long ptr = open(context.getPtr(), config);
- LOGGER.log(Level.FINE, "finished RocReceiver.open(), context ptr={0}, ptr={1}",
- new Object[]{toHex(context.getPtr()), toHex(ptr)});
- return ptr;
+ long ptr = nativeOpen(context.getPtr(), config);
+
+ LOGGER.log(Level.FINE, "leaving RocReceiver(), ptr={0}", toHex(ptr));
+ return ptr;
+ } catch (Exception exc) {
+ LOGGER.log(Level.SEVERE, "exception in RocReceiver(), exception={0}", exc);
+ throw exc;
+ }
}
- /**
- * Destruct native object
- */
- private static void destroy(long ptr, RocContext context) throws Exception {
- LOGGER.log(Level.FINE, "starting RocReceiver.close(), context ptr={0}, ptr={1}",
- new Object[]{toHex(context.getPtr()), toHex(ptr)});
- close(ptr);
- LOGGER.log(Level.FINE, "finished RocReceiver.close(), context ptr={0}, ptr={1}",
- new Object[]{toHex(context.getPtr()), toHex(ptr)});
+ private static void destroy(long ptr, RocContext context) {
+ try {
+ LOGGER.log(Level.FINE, "entering RocReceiver.close(), ptr={0}", toHex(ptr));
+
+ nativeClose(ptr);
+
+ LOGGER.log(Level.FINE, "leaving RocReceiver.close(), ptr={0}", toHex(ptr));
+ } catch (RuntimeException exc) {
+ LOGGER.log(Level.SEVERE, "exception in RocReceiver.close(), ptr={0}, exception={1}",
+ new Object[]{toHex(context.getPtr()), toHex(ptr), exc});
+ throw exc;
+ }
}
/**
@@ -209,120 +232,166 @@ private static void destroy(long ptr, RocContext context) throws Exception {
* endpoint has explicitly set zero port, the receiver is bound to a randomly
- * chosen ephemeral port. If the function succeeds, the actual port to which the
- * receiver was bound is written back to endpoint.
+ * If an error happens during bind, the whole slot is disabled and marked
+ * broken. The slot index remains reserved. The user is responsible for
+ * removing the slot using {@link RocReceiver#unlink()}, after which slot
+ * index can be reused.
+ * float array which will be
- * filled with samples.
- * @throws IllegalArgumentException if the arguments are invalid.
- * @throws IOException if there are not enough resources.
+ * @throws IllegalArgumentException if the arguments are invalid.
+ * @throws RocException if operation failed.
*/
- public void read(float[] samples) throws IllegalArgumentException, IOException {
+ public void read(float[] samples) throws RocException {
Check.notNull(samples, "samples");
- readFloats(getPtr(), samples);
- }
-
- private static native long open(long contextPtr, RocReceiverConfig config) throws IllegalArgumentException, Exception;
- private native void setMulticastGroup(long receiverPtr, int slot, int iface, String ip) throws IllegalArgumentException;
+ nativeReadFloats(getPtr(), samples);
+ }
- private native void bind(long receiverPtr, int slot, int iface, Endpoint endpoint) throws IllegalArgumentException, IOException;
+ private static native long nativeOpen(long contextPtr, RocReceiverConfig config) throws RocException;
+ private static native void nativeClose(long receiverPtr);
- private native void readFloats(long receiverPtr, float[] samples) throws IOException;
+ private native void nativeConfigure(long receiverPtr, int slot, int iface, InterfaceConfig config) throws RocException;
+ private native void nativeBind(long receiverPtr, int slot, int iface, Endpoint endpoint) throws RocException;
+ private native void nativeUnlink(long receiverPtr, int slot) throws RocException;
- private static native void close(long receiverPtr) throws IOException;
+ private native void nativeReadFloats(long receiverPtr, float[] samples) throws RocException;
}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/RocReceiverConfig.java b/src/main/java/org/rocstreaming/roctoolkit/RocReceiverConfig.java
index f9f9729b..5cc82669 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/RocReceiverConfig.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/RocReceiverConfig.java
@@ -1,3 +1,6 @@
+// Code generated by bindgen.py from roc-streaming/bindgen
+// roc-toolkit git tag: v0.3.0, commit: 57b932b8
+
package org.rocstreaming.roctoolkit;
import java.time.Duration;
@@ -17,117 +20,96 @@
public class RocReceiverConfig {
/**
- * The rate of the samples in the frames returned to the user.
- * Number of samples per channel per second.
- * Should be set to a positive value.
+ * The encoding used in frames returned by receiver.
+ *
- *
+ * Resampler profile.
+ * breakageDetectionWindow
- * during brokenPlaybackTimeout.
- * This mechanism allows to detect vicious circles like when all
- * client packets are a bit late and receiver constantly drops them
- * producing unpleasant noise.
- * If zero or unset, default value is used.
- * If negative, the timeout is disabled.
+ * Timeout for choppy playback, in nanoseconds.
+ * RocReceiverConfigValidator adds validation to RocReceiverConfig builder.
+ * Adds validation to {@link RocReceiverConfig} builder.
*/
class RocReceiverConfigValidator extends RocReceiverConfig.Builder {
@Override
public RocReceiverConfig build() {
RocReceiverConfig config = super.build();
- Check.notNegative(config.getFrameSampleRate(), "frameSampleRate");
- Check.notNull(config.getFrameChannels(), "frameChannels");
- Check.notNull(config.getFrameEncoding(), "frameEncoding");
- Check.notNegative(config.getTargetLatency(), "targetLatency");
- Check.notNegative(config.getMaxLatencyOverrun(), "maxLatencyOverrun");
- Check.notNegative(config.getMaxLatencyUnderrun(), "maxLatencyUnderrun");
- Check.notNegative(config.getBreakageDetectionWindow(), "breakageDetectionWindow");
+
+ Check.notNull(config.getFrameEncoding(), "RocReceiverConfig.frameEncoding");
+ Check.notNegative(config.getTargetLatency(), "RocReceiverConfig.targetLatency");
+ Check.notNegative(config.getLatencyTolerance(), "RocReceiverConfig.latencyTolerance");
+
return config;
}
}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/RocSender.java b/src/main/java/org/rocstreaming/roctoolkit/RocSender.java
index 0ed1f766..a737a647 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/RocSender.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/RocSender.java
@@ -1,140 +1,152 @@
package org.rocstreaming.roctoolkit;
-import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
- * Sender peer.
+ * Sender node.
* Context
* Lifecycle
- *
- *
+ *
+ * Life cycle
* RocSender class implements {@link AutoCloseable AutoCloseable} so if it is used in a
- * try-with-resources statement the object is closed automatically at the end of the statement.
+ *
+ *
+ *
*
* Slots, interfaces, and endpoints
*
- *
*
- *
+ * FEC scheme
*
- *
* Sample rate
* Clock source
*
- *
+ *
+ *
*
- * Thread-safety
+ *
+ * Thread safety
* Example
*
* {@code
- * RocSenderConfig config = RocSenderConfig.builder()
- * .frameSampleRate(SAMPLE_RATE)
- * .frameChannels(ChannelSet.STEREO)
- * .frameEncoding(FrameEncoding.PCM_FLOAT)
- * .resamplerProfile(ResamplerProfile.DISABLE)
- * .fecEncoding(FecEncoding.RS8M)
- * .build();
+ * RocSenderConfig senderConfig = RocSenderConfig.builder()
+ * .frameEncoding(
+ * MediaEncoding.builder()
+ * .rate(44100)
+ * .format(Format.PCM_FLOAT32)
+ * .channels(ChannelLayout.STEREO)
+ * .build()
+ * )
+ * .fecEncoding(FecEncoding.RS8M)
+ * .clockSource(ClockSource.INTERNAL)
+ * .build();
* try (
* RocContext context = new RocContext();
- * RocSender sender = new RocSender(context, config);
+ * RocSender sender = new RocSender(context, senderConfig);
* ) {
* sender.connect(Slot.DEFAULT, Interface.AUDIO_SOURCE, new Endpoint("rtp+rs8m://0.0.0.0:10001"));
* sender.connect(Slot.DEFAULT, Interface.AUDIO_REPAIR, new Endpoint("rs8m://0.0.0.0:10002"));
@@ -144,6 +156,7 @@
* }
*
*
+ *
* @see RocContext
* @see RocSenderConfig
* @see java.lang.AutoCloseable
@@ -152,153 +165,197 @@ public class RocSender extends NativeObject {
private static final Logger LOGGER = Logger.getLogger(RocSender.class.getName());
- /**
- * Validate sender constructor parameters and open a new sender if validation is successful.
- *
- * @param context should point to an opened context.
- * @param config should point to an initialized config.
- * @return the native roc sender pointer.
- * @throws IllegalArgumentException if the arguments are invalid.
- * @throws Exception if an error occurred when creating the sender.
- */
- private static long construct(RocContext context, RocSenderConfig config) throws IllegalArgumentException, Exception {
- Check.notNull(context, "context");
- Check.notNull(config, "config");
+ private static long construct(RocContext context, RocSenderConfig config) throws RocException {
+ Check.notNull(context, "RocContext");
+ Check.notNull(config, "RocSenderConfig");
+
+ try {
+ LOGGER.log(Level.FINE, "entering RocSender(), contextPtr={0}, config={1}",
+ new Object[]{toHex(context.getPtr()), config});
- LOGGER.log(Level.FINE, "starting RocSender.open(), context ptr={0}, config={1}",
- new Object[]{toHex(context.getPtr()), config});
- long ptr = open(context.getPtr(), config);
- LOGGER.log(Level.FINE, "finished RocSender.open(), context ptr={0}, ptr={1}",
- new Object[]{toHex(context.getPtr()), toHex(ptr)});
- return ptr;
+ long ptr = nativeOpen(context.getPtr(), config);
+
+ LOGGER.log(Level.FINE, "leaving RocSender(), ptr={0}", toHex(ptr));
+ return ptr;
+ } catch (Exception exc) {
+ LOGGER.log(Level.SEVERE, "exception in RocSender(), exception={0}", exc);
+ throw exc;
+ }
}
- /**
- * Destruct native object
- */
- private static void destroy(long ptr, RocContext context) throws Exception {
- LOGGER.log(Level.FINE, "starting RocSender.close(), context ptr={0}, ptr={1}",
- new Object[]{toHex(context.getPtr()), toHex(ptr)});
- close(ptr);
- LOGGER.log(Level.FINE, "finished RocSender.close(), context ptr={0}, ptr={1}",
- new Object[]{toHex(context.getPtr()), toHex(ptr)});
+ private static void destroy(long ptr, RocContext context) {
+ try {
+ LOGGER.log(Level.FINE, "entering RocSender.close(), ptr={0}", toHex(ptr));
+
+ nativeClose(ptr);
+
+ LOGGER.log(Level.FINE, "leaving RocSender.close(), ptr={0}", toHex(ptr));
+ } catch (RuntimeException exc) {
+ LOGGER.log(Level.SEVERE, "exception in RocSender.close(), ptr={0}, exception={1}",
+ new Object[]{toHex(context.getPtr()), toHex(ptr), exc});
+ throw exc;
+ }
}
/**
* Open a new sender.
* Allocates and initializes a new sender, and attaches it to the context.
*
- * @param context should point to an opened context.
- * @param config should point to an initialized config.
- * @throws IllegalArgumentException if the arguments are invalid.
- * @throws Exception if an error occurred when creating the sender.
+ * @param context should point to an opened context.
+ * @param config should point to an initialized config.
+ *
+ * @throws IllegalArgumentException if the arguments are invalid.
+ * @throws RocException if operation failed.
*/
- public RocSender(RocContext context, RocSenderConfig config) throws IllegalArgumentException, Exception {
+ public RocSender(RocContext context, RocSenderConfig config) throws RocException {
super(construct(context, config), context, ptr -> destroy(ptr, context));
}
/**
- * Set sender interface outgoing address.
+ * Set sender interface configuration.
* RocSenderConfigValidator adds validation to RocSenderConfig builder.
+ * Adds validation to {@link RocSenderConfig} builder.
*/
class RocSenderConfigValidator extends RocSenderConfig.Builder {
@Override
public RocSenderConfig build() {
RocSenderConfig config = super.build();
- Check.notNegative(config.getFrameSampleRate(), "frameSampleRate");
- Check.notNull(config.getFrameChannels(), "frameChannels");
- Check.notNull(config.getFrameEncoding(), "frameEncoding");
- Check.notNegative(config.getPacketSampleRate(), "packetSampleRate");
- Check.notNegative(config.getPacketLength(), "packetLength");
- Check.notNegative(config.getFecBlockSourcePackets(), "fecBlockSourcePackets");
- Check.notNegative(config.getFecBlockRepairPackets(), "fecBlockRepairPackets");
+
+ Check.notNull(config.getFrameEncoding(), "RocSenderConfig.frameEncoding");
+ Check.notNegative(config.getPacketLength(), "RocSenderConfig.packetLength");
+ Check.notNegative(config.getFecBlockSourcePackets(), "RocSenderConfig.fecBlockSourcePackets");
+ Check.notNegative(config.getFecBlockRepairPackets(), "RocSenderConfig.fecBlockRepairPackets");
+
return config;
}
}
diff --git a/src/main/java/org/rocstreaming/roctoolkit/Slot.java b/src/main/java/org/rocstreaming/roctoolkit/Slot.java
index 11545b32..11fd47fd 100644
--- a/src/main/java/org/rocstreaming/roctoolkit/Slot.java
+++ b/src/main/java/org/rocstreaming/roctoolkit/Slot.java
@@ -39,6 +39,6 @@ public int getValue() {
@Override
public String toString() {
- return "Slot(" + value + ')';
+ return "Slot(" + value + ")";
}
}
diff --git a/src/test/java/org/rocstreaming/roctoolkit/BaseTest.java b/src/test/java/org/rocstreaming/roctoolkit/BaseTest.java
index cea6e7bc..8f0b80ab 100644
--- a/src/test/java/org/rocstreaming/roctoolkit/BaseTest.java
+++ b/src/test/java/org/rocstreaming/roctoolkit/BaseTest.java
@@ -1,17 +1,35 @@
package org.rocstreaming.roctoolkit;
+import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
-import java.io.IOException;
import java.io.InputStream;
+import java.util.logging.Level;
import java.util.logging.LogManager;
public class BaseTest {
+ static private Level originalLogLevel;
+
@BeforeAll
- static void configureLogger() throws IOException {
+ static void configureLogger() throws Exception {
try (InputStream is = RocReceiverTest.class.getClassLoader().getResourceAsStream("logging.properties")) {
LogManager.getLogManager().readConfiguration(is);
}
+
+ originalLogLevel = RocLogger.LOGGER.getLevel();
+
+ String gradleLogLevel = System.getProperty("org.gradle.logging.level");
+
+ if (gradleLogLevel == null || gradleLogLevel.equals("INFO") || gradleLogLevel.equals("DEBUG")) {
+ RocLogger.LOGGER.setLevel(Level.FINE);
+ } else {
+ RocLogger.LOGGER.setLevel(Level.OFF);
+ }
+ }
+
+ @AfterAll
+ static public void restoreLogger() {
+ RocLogger.LOGGER.setLevel(originalLogLevel);
}
}
diff --git a/src/test/java/org/rocstreaming/roctoolkit/EndpointTest.java b/src/test/java/org/rocstreaming/roctoolkit/EndpointTest.java
index 2a0973a3..197a36b2 100644
--- a/src/test/java/org/rocstreaming/roctoolkit/EndpointTest.java
+++ b/src/test/java/org/rocstreaming/roctoolkit/EndpointTest.java
@@ -149,7 +149,7 @@ public static List