diff --git a/srtdroid-ktx/src/main/java/io/github/thibaultbee/srtdroid/ktx/CoroutineSocket.kt b/srtdroid-ktx/src/main/java/io/github/thibaultbee/srtdroid/ktx/CoroutineSocket.kt index 569a04e4..7ee4deec 100644 --- a/srtdroid-ktx/src/main/java/io/github/thibaultbee/srtdroid/ktx/CoroutineSocket.kt +++ b/srtdroid-ktx/src/main/java/io/github/thibaultbee/srtdroid/ktx/CoroutineSocket.kt @@ -6,6 +6,7 @@ import io.github.thibaultbee.srtdroid.enums.EpollOpt import io.github.thibaultbee.srtdroid.enums.ErrorType import io.github.thibaultbee.srtdroid.enums.SockOpt import io.github.thibaultbee.srtdroid.enums.SockStatus +import io.github.thibaultbee.srtdroid.interfaces.ConfigurableSocket import io.github.thibaultbee.srtdroid.models.Epoll import io.github.thibaultbee.srtdroid.models.Error import io.github.thibaultbee.srtdroid.models.MsgCtrl @@ -48,7 +49,7 @@ class CoroutineSocket private constructor( private val socket: Socket ) : - CoroutineScope { + ConfigurableSocket, CoroutineScope { constructor() : this(Socket()) init { @@ -292,7 +293,7 @@ private constructor( * @throws IOException if can't get [SockOpt] * @see [setSockFlag] */ - fun getSockFlag(opt: SockOpt): Any { + override fun getSockFlag(opt: SockOpt): Any { return socket.getSockFlag(opt) } @@ -306,7 +307,7 @@ private constructor( * @throws IOException if can't set [SockOpt] * @see [getSockFlag] */ - fun setSockFlag(opt: SockOpt, value: Any) { + override fun setSockFlag(opt: SockOpt, value: Any) { if ((opt == SockOpt.RCVSYN) || (opt == SockOpt.SNDSYN)) { throw IllegalArgumentException("Options not supported") } diff --git a/srtdroid-ktx/src/main/java/io/github/thibaultbee/srtdroid/ktx/extensions/CoroutineSocketExtensions.kt b/srtdroid-ktx/src/main/java/io/github/thibaultbee/srtdroid/ktx/extensions/CoroutineSocketExtensions.kt index d83bbf1c..8310dad5 100644 --- a/srtdroid-ktx/src/main/java/io/github/thibaultbee/srtdroid/ktx/extensions/CoroutineSocketExtensions.kt +++ b/srtdroid-ktx/src/main/java/io/github/thibaultbee/srtdroid/ktx/extensions/CoroutineSocketExtensions.kt @@ -1,7 +1,7 @@ package io.github.thibaultbee.srtdroid.ktx.extensions -import io.github.thibaultbee.srtdroid.enums.SockStatus import io.github.thibaultbee.srtdroid.ktx.CoroutineSocket +import io.github.thibaultbee.srtdroid.models.SrtUrl import java.io.File import java.net.BindException import java.net.ConnectException @@ -10,15 +10,36 @@ import java.net.InetSocketAddress import java.net.SocketException import java.net.SocketTimeoutException +/** + * Binds the socket to a local address. + * + * **See Also:** [srt_bind](https://github.com/Haivision/srt/blob/master/docs/API/API-functions.md#srt_bind) + * + * @param url the URL to bind to in FFmpeg format srt://hostname:port[?options] + * + * @throws BindException if bind has failed + */ +suspend fun CoroutineSocket.bind(url: String) = bind(SrtUrl(url)) /** - * Tests if the SRT socket is connected. + * Binds the socket to a local address. + * + * **See Also:** [srt_bind](https://github.com/Haivision/srt/blob/master/docs/API/API-functions.md#srt_bind) + * + * @param srtUrl the URL to bind to in FFmpeg format srt://hostname:port[?options] * - * @return true if the SRT socket is connected, otherwise false + * @throws BindException if bind has failed */ -val CoroutineSocket.isConnected: Boolean - get() = sockState == SockStatus.CONNECTED +suspend fun CoroutineSocket.bind(srtUrl: SrtUrl) { + if (srtUrl.mode != null) { + require(srtUrl.mode != SrtUrl.Mode.CALLER) { "Bind is only for `listener` or `rendezvous` mode but ${srtUrl.mode}" } + } + srtUrl.preApplyTo(this) + srtUrl.preBindApplyTo(this) + bind(srtUrl.hostname, srtUrl.port) + srtUrl.postApplyTo(this) +} /** * Binds the socket to a local address. @@ -45,6 +66,35 @@ suspend fun CoroutineSocket.bind(address: String, port: Int) = suspend fun CoroutineSocket.bind(address: InetAddress, port: Int) = bind(InetSocketAddress(address, port)) + +/** + * Connects a socket to an URL. + * + * **See Also:** [srt_connect](https://github.com/Haivision/srt/blob/master/docs/API/API-functions.md#srt_connect) + * + * @param url the URL to connect to in FFmpeg format srt://hostname:port[?options] + * @throws ConnectException if connection has failed + */ +suspend fun CoroutineSocket.connect(url: String) = connect(SrtUrl(url)) + +/** + * Connects a socket to an URL. + * + * **See Also:** [srt_connect](https://github.com/Haivision/srt/blob/master/docs/API/API-functions.md#srt_connect) + * + * @param srtUrl the URL to connect to in FFmpeg format srt://hostname:port[?options] + * @throws ConnectException if connection has failed + */ +suspend fun CoroutineSocket.connect(srtUrl: SrtUrl) { + if (srtUrl.mode != null) { + require(srtUrl.mode != SrtUrl.Mode.LISTENER) { "Connect is only for `caller` or `rendezvous` mode but ${srtUrl.mode}" } + } + + srtUrl.preApplyTo(this) + connect(srtUrl.hostname, srtUrl.port) + srtUrl.postApplyTo(this) +} + /** * Connects a socket to a specified address and port. * @@ -69,6 +119,35 @@ suspend fun CoroutineSocket.connect(address: String, port: Int) = suspend fun CoroutineSocket.connect(address: InetAddress, port: Int) = connect(InetSocketAddress(address, port)) +/** + * Performs a rendezvous connection. + * + * **See Also:** [srt_rendezvous](https://github.com/Haivision/srt/blob/master/docs/API/API-functions.md#srt_rendezvous) + * + * @param url the URL to rendezvous to in FFmpeg format srt://hostname:port[?options] + * @throws SocketException if rendezvous connection has failed + */ +suspend fun CoroutineSocket.rendezVous(url: String) = rendezVous(SrtUrl(url)) + +/** + * Performs a rendezvous connection. + * + * **See Also:** [srt_rendezvous](https://github.com/Haivision/srt/blob/master/docs/API/API-functions.md#srt_rendezvous) + * + * @param srtUrl the URL to rendezvous to in FFmpeg format srt://hostname:port[?options] + * @throws SocketException if rendezvous connection has failed + */ +suspend fun CoroutineSocket.rendezVous( + srtUrl: SrtUrl +) { + if (srtUrl.mode != null) { + require(srtUrl.mode == SrtUrl.Mode.RENDEZ_VOUS) { "Connect is only for `caller` or `rendezvous` mode but ${srtUrl.mode}" } + } + + srtUrl.preApplyTo(this) + rendezVous(srtUrl.hostname, srtUrl.hostname, srtUrl.port) + srtUrl.postApplyTo(this) +} /** * Performs a rendezvous connection. diff --git a/srtdroid/src/main/java/io/github/thibaultbee/srtdroid/extensions/SocketExtensions.kt b/srtdroid/src/main/java/io/github/thibaultbee/srtdroid/extensions/SocketExtensions.kt index e183025d..aa13e9f1 100644 --- a/srtdroid/src/main/java/io/github/thibaultbee/srtdroid/extensions/SocketExtensions.kt +++ b/srtdroid/src/main/java/io/github/thibaultbee/srtdroid/extensions/SocketExtensions.kt @@ -28,7 +28,7 @@ fun Socket.bind(url: String) = bind(SrtUrl(url)) */ fun Socket.bind(srtUrl: SrtUrl) { if (srtUrl.mode != null) { - require(srtUrl.mode != SrtUrl.Mode.CALLER) { "Bind is only for `listener` or `rendezvous` mode but ${srtUrl.mode.value}" } + require(srtUrl.mode != SrtUrl.Mode.CALLER) { "Bind is only for `listener` or `rendezvous` mode but ${srtUrl.mode}" } } srtUrl.preApplyTo(this) @@ -57,7 +57,7 @@ fun Socket.connect(url: String) = connect(SrtUrl(url)) */ fun Socket.connect(srtUrl: SrtUrl) { if (srtUrl.mode != null) { - require(srtUrl.mode != SrtUrl.Mode.LISTENER) { "Connect is only for `caller` or `rendezvous` mode but ${srtUrl.mode.value}" } + require(srtUrl.mode != SrtUrl.Mode.LISTENER) { "Connect is only for `caller` or `rendezvous` mode but ${srtUrl.mode}" } } srtUrl.preApplyTo(this) @@ -87,7 +87,7 @@ fun Socket.rendezVous( srtUrl: SrtUrl ) { if (srtUrl.mode != null) { - require(srtUrl.mode == SrtUrl.Mode.RENDEZ_VOUS) { "Connect is only for `caller` or `rendezvous` mode but ${srtUrl.mode.value}" } + require(srtUrl.mode == SrtUrl.Mode.RENDEZ_VOUS) { "Connect is only for `caller` or `rendezvous` mode but ${srtUrl.mode}" } } srtUrl.preApplyTo(this) diff --git a/srtdroid/src/main/java/io/github/thibaultbee/srtdroid/interfaces/ConfigurableSocket.kt b/srtdroid/src/main/java/io/github/thibaultbee/srtdroid/interfaces/ConfigurableSocket.kt new file mode 100644 index 00000000..f0888093 --- /dev/null +++ b/srtdroid/src/main/java/io/github/thibaultbee/srtdroid/interfaces/ConfigurableSocket.kt @@ -0,0 +1,11 @@ +package io.github.thibaultbee.srtdroid.interfaces + +import io.github.thibaultbee.srtdroid.enums.SockOpt + +/** + * A convenient interface to get and set socket options + */ +interface ConfigurableSocket { + fun getSockFlag(opt: SockOpt): Any + fun setSockFlag(opt: SockOpt, value: Any) +} \ No newline at end of file diff --git a/srtdroid/src/main/java/io/github/thibaultbee/srtdroid/models/Socket.kt b/srtdroid/src/main/java/io/github/thibaultbee/srtdroid/models/Socket.kt index d19ed876..a9132733 100644 --- a/srtdroid/src/main/java/io/github/thibaultbee/srtdroid/models/Socket.kt +++ b/srtdroid/src/main/java/io/github/thibaultbee/srtdroid/models/Socket.kt @@ -21,6 +21,7 @@ import io.github.thibaultbee.srtdroid.enums.ErrorType import io.github.thibaultbee.srtdroid.enums.RejectReasonCode import io.github.thibaultbee.srtdroid.enums.SockOpt import io.github.thibaultbee.srtdroid.enums.SockStatus +import io.github.thibaultbee.srtdroid.interfaces.ConfigurableSocket import io.github.thibaultbee.srtdroid.models.rejectreason.InternalRejectReason import io.github.thibaultbee.srtdroid.models.rejectreason.PredefinedRejectReason import io.github.thibaultbee.srtdroid.models.rejectreason.RejectReason @@ -45,7 +46,7 @@ import java.nio.ByteBuffer * Once it has been called, you must release Srt context with [Srt.cleanUp] when application leaves. */ class Socket -private constructor(private val srtsocket: Int) : Closeable { +private constructor(private val srtsocket: Int) : ConfigurableSocket, Closeable { companion object { @JvmStatic private external fun nativeCreateSocket(): Int @@ -412,7 +413,7 @@ private constructor(private val srtsocket: Int) : Closeable { * @throws IOException if can't get [SockOpt] * @see [setSockFlag] */ - fun getSockFlag(opt: SockOpt): Any { + override fun getSockFlag(opt: SockOpt): Any { return nativeGetSockFlag(opt) ?: throw IOException(Error.lastErrorMessage) } @@ -428,7 +429,7 @@ private constructor(private val srtsocket: Int) : Closeable { * @throws IOException if can't set [SockOpt] * @see [getSockFlag] */ - fun setSockFlag(opt: SockOpt, value: Any) { + override fun setSockFlag(opt: SockOpt, value: Any) { if (nativeSetSockFlag(opt, value) != 0) { throw IOException(Error.lastErrorMessage) } diff --git a/srtdroid/src/main/java/io/github/thibaultbee/srtdroid/models/SrtUrl.kt b/srtdroid/src/main/java/io/github/thibaultbee/srtdroid/models/SrtUrl.kt index da260936..30c34438 100644 --- a/srtdroid/src/main/java/io/github/thibaultbee/srtdroid/models/SrtUrl.kt +++ b/srtdroid/src/main/java/io/github/thibaultbee/srtdroid/models/SrtUrl.kt @@ -21,6 +21,7 @@ import io.github.thibaultbee.srtdroid.Srt import io.github.thibaultbee.srtdroid.enums.SockOpt import io.github.thibaultbee.srtdroid.enums.Transtype import io.github.thibaultbee.srtdroid.extensions.toBoolean +import io.github.thibaultbee.srtdroid.interfaces.ConfigurableSocket import java.security.InvalidParameterException /** @@ -197,7 +198,11 @@ data class SrtUrl( } } - internal fun preBindApplyTo(socket: Socket) { + /** + * Sets pre configuration for binding socket. + * Internal purpose only. + */ + fun preBindApplyTo(socket: ConfigurableSocket) { iptos?.let { socket.setSockFlag(SockOpt.IPTOS, it) } ipttl?.let { socket.setSockFlag(SockOpt.IPTTL, it) } maxSegmentSize?.let { socket.setSockFlag(SockOpt.MSS, it) } @@ -207,7 +212,11 @@ data class SrtUrl( recvBufferSize?.let { socket.setSockFlag(SockOpt.RCVBUF, it) } } - internal fun preApplyTo(socket: Socket) { + /** + * Sets pre configuration for socket. + * Internal purpose only. + */ + fun preApplyTo(socket: ConfigurableSocket) { connectTimeoutInMs?.let { socket.setSockFlag(SockOpt.CONNTIMEO, it) } flightFlagSize?.let { socket.setSockFlag(SockOpt.FC, it) } @@ -236,7 +245,11 @@ data class SrtUrl( enableTimestampBasedPacketDelivery?.let { socket.setSockFlag(SockOpt.TSBPDMODE, it) } } - internal fun postApplyTo(socket: Socket) { + /** + * Sets post configuration for socket. + * Internal purpose only. + */ + fun postApplyTo(socket: ConfigurableSocket) { inputBandwidth?.let { socket.setSockFlag(SockOpt.INPUTBW, it) } maxBandwidth?.let { socket.setSockFlag(SockOpt.MAXBW, it) } overheadBandwidth?.let { socket.setSockFlag(SockOpt.OHEADBW, it) }