) {
+ val endpoint = remoteEndpoints[endpointId] ?: run {
+ Timber.e("Failed to process TracksRemoved event: Endpoint not found: $endpointId")
return
}
@@ -365,16 +370,16 @@ constructor(
this.listener.onTrackRemoved(context)
}
- val updatedPeer = trackIds.fold(peer) { acc, trackId ->
+ val updatedEndpoint = trackIds.fold(endpoint) { acc, trackId ->
acc.withoutTrack(trackId)
}
- remotePeers[peerId] = updatedPeer
+ remoteEndpoints[endpointId] = updatedEndpoint
}
- override fun onTrackUpdated(peerId: String, trackId: String, metadata: Metadata) {
- val peer = remotePeers[peerId] ?: run {
- Timber.e("Failed to process TrackUpdated event: Peer not found: $peerId")
+ override fun onTrackUpdated(endpointId: String, trackId: String, metadata: Metadata) {
+ val endpoint = remoteEndpoints[endpointId] ?: run {
+ Timber.e("Failed to process TrackUpdated event: Endpoint not found: $endpointId")
return
}
@@ -385,16 +390,16 @@ constructor(
context.metadata = metadata
- val updatedPeer = peer
+ val updatedEndpoint = endpoint
.withoutTrack(trackId)
.withTrack(trackId, metadata)
- remotePeers[peerId] = updatedPeer
+ remoteEndpoints[endpointId] = updatedEndpoint
this.listener.onTrackUpdated(context)
}
- override fun onTrackEncodingChanged(peerId: String, trackId: String, encoding: String, encodingReason: String) {
+ override fun onTrackEncodingChanged(endpointId: String, trackId: String, encoding: String, encodingReason: String) {
val encodingReasonEnum = EncodingReason.fromString(encodingReason)
if (encodingReasonEnum == null) {
Timber.e("Invalid encoding reason: $encodingReason")
@@ -411,15 +416,6 @@ constructor(
return
}
trackContext.setEncoding(encodingEnum, encodingReasonEnum)
- this.listener.onTrackEncodingChanged(peerId, trackId, encoding)
- }
-
- override fun onRemoved(peerId: String, reason: String) {
- if (peerId != localPeer.id) {
- Timber.e("Received onRemoved media event, but it does not refer to the local peer")
- return
- }
- listener.onRemoved(reason)
}
override fun onVadNotification(trackId: String, status: String) {
@@ -440,18 +436,6 @@ constructor(
listener.onBandwidthEstimationChanged(estimation)
}
- override fun onError(error: EventTransportError) {
- if (error is EventTransportError.ConnectionError) {
- listener.onError(MembraneRTCError.Transport(error.reason))
- } else {
- listener.onError(MembraneRTCError.Transport(error.message ?: "unknown transport message"))
- }
- }
-
- override fun onClose() {
- Timber.i("Transport has been closed")
- }
-
fun setTargetTrackEncoding(trackId: String, encoding: TrackEncoding) {
coroutineScope.launch {
rtcEngineCommunication.setTargetTrackEncoding(trackId, encoding)
diff --git a/MembraneRTC/src/main/java/org/membraneframework/rtc/MembraneRTC.kt b/MembraneRTC/src/main/java/org/membraneframework/rtc/MembraneRTC.kt
index c7b56b2..d47a64b 100644
--- a/MembraneRTC/src/main/java/org/membraneframework/rtc/MembraneRTC.kt
+++ b/MembraneRTC/src/main/java/org/membraneframework/rtc/MembraneRTC.kt
@@ -7,6 +7,7 @@ import org.membraneframework.rtc.dagger.DaggerMembraneRTCComponent
import org.membraneframework.rtc.media.*
import org.membraneframework.rtc.models.RTCStats
import org.membraneframework.rtc.utils.Metadata
+import org.membraneframework.rtc.utils.SerializedMediaEvent
import org.webrtc.Logging
/**
@@ -14,11 +15,11 @@ import org.webrtc.Logging
*
* The client is responsible for relaying MembraneRTC Engine specific messages through given reliable transport layer.
* Once initialized, the client is responsible for exchanging necessary messages via provided EventTransport passed via `ConnectOptions` and managing underlying
- * `PeerConnection`. The goal of the client is to be as lean as possible, meaning that all activities regarding the session such as moderating
+ * `EndpointConnection`. The goal of the client is to be as lean as possible, meaning that all activities regarding the session such as moderating
* should be implemented by the user himself on top of the MembraneRTC.
*
- * The user's ability of interacting with the client is greatly limited to the essential actions such as joining/leaving the session,
- * adding/removing local tracks and receiving information about remote peers and their tracks that can be played by the user.
+ * The user's ability of interacting with the client is greatly limited to the essential actions such as connecting to/leaving the session,
+ * adding/removing local tracks and receiving information about remote endpoints and their tracks that can be played by the user.
*
* User can request 3 different types of local tracks that will get forwarded to the server by the client:
*
@@ -27,24 +28,25 @@ import org.webrtc.Logging
* - `LocalScreencast` - a screencast track capturing a device's screen using MediaProjection mechanism
*
*
- * It is recommended to request necessary audio and video tracks before joining the room but it does not mean it can't be done afterwards (in case of screencast)
+ * It is recommended to request necessary audio and video tracks before connecting to the room but it does not mean it can't be done afterwards (in case of screencast)
*
- * Once the user received onConnected notification they can call the join method to initialize joining the session.
- * After receiving `onJoinSuccess` a user will receive notification about various peers joining/leaving the session, new tracks being published and ready for playback
+ * Once the user created MembraneRTC client, they can call the connect method to initialize connecting to the session.
+ * After receiving `onConnected` a user will receive notification about various endpoints connecting to/leaving the session, new tracks being published and ready for playback
* or going inactive.
*/
class MembraneRTC
private constructor(
private var client: InternalMembraneRTC
) {
-
/**
- * Starts the join process.
+ * Tries to connect the RTC Engine. If user is accepted then onConnected will be called.
+ * In other case {@link Callbacks.onConnectError} is invoked.
*
- * Should be called only when a listener received onConnected message.
+ * @param endpointMetadata - Any information that other endpoints will receive in onEndpointAdded
+ * after accepting this endpoint
*/
- fun join() {
- client.join()
+ fun connect(endpointMetadata: Metadata) {
+ client.connect(endpointMetadata)
}
/**
@@ -56,6 +58,17 @@ private constructor(
client.disconnect()
}
+ /**
+ * Feeds media event received from RTC Engine to MembraneWebRTC.
+ * This function should be called whenever some media event from RTC Engine
+ * was received and can result in MembraneWebRTC generating some other
+ * media events.
+ * @param mediaEvent - String data received over custom signalling layer.
+ */
+ fun receiveMediaEvent(mediaEvent: SerializedMediaEvent) {
+ client.receiveMediaEvent(mediaEvent)
+ }
+
/**
* Creates a video track utilizing device's camera.
*
@@ -152,23 +165,23 @@ private constructor(
}
/**
- * Updates the metadata for the current peer.
- * @param peerMetadata Data about this peer that other peers will receive upon joining.
+ * Updates the metadata for the current endpoint.
+ * @param endpointMetadata Data about this endpoint that other endpoints will receive upon connecting.
*
* If the metadata is different from what is already tracked in the room, the optional
- * callback `onPeerUpdated` will be triggered for other peers in the room.
+ * callback `onEndpointUpdated` will be triggered for other endpoints in the room.
*/
- fun updatePeerMetadata(peerMetadata: Metadata) {
- client.updatePeerMetadata(peerMetadata)
+ fun updateEndpointMetadata(endpointMetadata: Metadata) {
+ client.updateEndpointMetadata(endpointMetadata)
}
/**
* Updates the metadata for a specific track.
* @param trackId local track id of audio or video track.
- * @param trackMetadata Data about this track that other peers will receive upon joining.
+ * @param trackMetadata Data about this track that other endpoints will receive upon connecting.
*
* If the metadata is different from what is already tracked in the room, the optional
- * callback `onTrackUpdated` will be triggered for other peers in the room.
+ * callback `onTrackUpdated` will be triggered for other endpoints in the room.
*/
fun updateTrackMetadata(trackId: String, trackMetadata: Metadata) {
client.updateTrackMetadata(trackId, trackMetadata)
@@ -217,14 +230,18 @@ private constructor(
companion object {
/**
- * Creates an instance of MembraneRTC client and starts the connecting process.
+ * Creates an instance of MembraneRTC client.
*
* @param appContext the context of the current application
- * @param options a set of options defining parameters such as event transport or connect metadata
* @param listener a listener that will receive all notifications emitted by the MembraneRTC
+ * @param options a set of options defining parameters such as encoder parameters
* @return an instance of the client in connecting state
*/
- fun connect(appContext: Context, options: ConnectOptions, listener: MembraneRTCListener): MembraneRTC {
+ fun create(
+ appContext: Context,
+ listener: MembraneRTCListener,
+ options: CreateOptions = CreateOptions()
+ ): MembraneRTC {
val ctx = appContext.applicationContext
val component = DaggerMembraneRTCComponent
@@ -235,8 +252,6 @@ private constructor(
.membraneRTCFactory()
.create(options, listener, Dispatchers.Default)
- client.connect()
-
return MembraneRTC(client)
}
}
diff --git a/MembraneRTC/src/main/java/org/membraneframework/rtc/MembraneRTCListener.kt b/MembraneRTC/src/main/java/org/membraneframework/rtc/MembraneRTCListener.kt
index 549e9d0..ff148d6 100644
--- a/MembraneRTC/src/main/java/org/membraneframework/rtc/MembraneRTCListener.kt
+++ b/MembraneRTC/src/main/java/org/membraneframework/rtc/MembraneRTCListener.kt
@@ -1,53 +1,43 @@
package org.membraneframework.rtc
-import org.membraneframework.rtc.models.Peer
+import org.membraneframework.rtc.models.Endpoint
import org.membraneframework.rtc.models.TrackContext
+import org.membraneframework.rtc.utils.SerializedMediaEvent
import timber.log.Timber
interface MembraneRTCListener {
- // / Callback invoked when client has successfully connected via transport layer.
- fun onConnected()
+ // Called each time MembraneWebRTC need to send some data to the server.
+ fun onSendMediaEvent(event: SerializedMediaEvent)
- // /Callback invoked when the client has been approved to participate in media exchange.
- fun onJoinSuccess(peerID: String, peersInRoom: List)
+ // Callback invoked when the client has been approved to participate in media exchange.
+ fun onConnected(endpointID: String, otherEndpoints: List)
- // /Callback invoked when client has been denied access to enter the room.
- fun onJoinError(metadata: Any)
+ // Called when endpoint of this MembraneRTC instance was removed
+ fun onDisconnected()
- // /Callback invoked a track is ready to be played.
+ // Called in case of errors related to multimedia session e.g. ICE connection.
+ fun onConnectError(metadata: Any)
+
+ // Callback invoked a track is ready to be played.
fun onTrackReady(ctx: TrackContext)
- // /Callback invoked a peer already present in a room adds a new track.
+ // Callback invoked a endpoint already present in a room adds a new track.
fun onTrackAdded(ctx: TrackContext)
- // /Callback invoked when a track will no longer receive any data.
+ // Callback invoked when a track will no longer receive any data.
fun onTrackRemoved(ctx: TrackContext)
- // /Callback invoked when track's metadata gets updated
+ // Callback invoked when track's metadata gets updated
fun onTrackUpdated(ctx: TrackContext)
- // /Callback invoked when a new peer joins the room.
- fun onPeerJoined(peer: Peer)
-
- // /Callback invoked when a peer leaves the room.
- fun onPeerLeft(peer: Peer)
-
- // /Callback invoked when peer's metadata gets updated.
- fun onPeerUpdated(peer: Peer)
-
- // Callback invoked when received track encoding has changed
- @Deprecated("Deprecated, use TrackContext::setOnEncodingChangedListener")
- fun onTrackEncodingChanged(peerId: String, trackId: String, encoding: String) {
- Timber.i(
- "Track encoding changed $trackId -> $encoding"
- )
- }
+ // Callback invoked when a new endpoint joins the room.
+ fun onEndpointAdded(endpoint: Endpoint)
- // /Callback invoked when an errors happens.
- fun onError(error: MembraneRTCError)
+ // Called each time endpoint is removed, called only for other endpoints.
+ fun onEndpointRemoved(endpoint: Endpoint)
- // Callback invoked every time a local peer is removed by the server
- fun onRemoved(reason: String) { Timber.e("Peer removed") }
+ // Called each time endpoint has its metadata updated.
+ fun onEndpointUpdated(endpoint: Endpoint)
// Called every time the server estimates client's bandwidth.
// estimation - client's available incoming bitrate estimated
diff --git a/MembraneRTC/src/main/java/org/membraneframework/rtc/PeerConnectionFactoryWrapper.kt b/MembraneRTC/src/main/java/org/membraneframework/rtc/PeerConnectionFactoryWrapper.kt
index 0b2932c..678e47f 100644
--- a/MembraneRTC/src/main/java/org/membraneframework/rtc/PeerConnectionFactoryWrapper.kt
+++ b/MembraneRTC/src/main/java/org/membraneframework/rtc/PeerConnectionFactoryWrapper.kt
@@ -7,11 +7,10 @@ import dagger.assisted.AssistedInject
import org.membraneframework.rtc.media.SimulcastVideoEncoderFactoryWrapper
import org.webrtc.*
import org.webrtc.audio.AudioDeviceModule
-import java.util.*
internal class PeerConnectionFactoryWrapper
@AssistedInject constructor(
- @Assisted private val connectOptions: ConnectOptions,
+ @Assisted private val createOptions: CreateOptions,
audioDeviceModule: AudioDeviceModule,
eglBase: EglBase,
appContext: Context
@@ -19,7 +18,7 @@ internal class PeerConnectionFactoryWrapper
@AssistedFactory
interface PeerConnectionFactoryWrapperFactory {
fun create(
- connectOptions: ConnectOptions
+ createOptions: CreateOptions
): PeerConnectionFactoryWrapper
}
@@ -34,7 +33,7 @@ internal class PeerConnectionFactoryWrapper
PeerConnectionFactory.builder().setAudioDeviceModule(audioDeviceModule).setVideoEncoderFactory(
SimulcastVideoEncoderFactoryWrapper(
eglBase.eglBaseContext,
- connectOptions.encoderOptions
+ createOptions.encoderOptions
)
).setVideoDecoderFactory(DefaultVideoDecoderFactory(eglBase.eglBaseContext)).createPeerConnectionFactory()
}
diff --git a/MembraneRTC/src/main/java/org/membraneframework/rtc/PeerConnectionManager.kt b/MembraneRTC/src/main/java/org/membraneframework/rtc/PeerConnectionManager.kt
index cbabcf0..a6970d2 100644
--- a/MembraneRTC/src/main/java/org/membraneframework/rtc/PeerConnectionManager.kt
+++ b/MembraneRTC/src/main/java/org/membraneframework/rtc/PeerConnectionManager.kt
@@ -51,6 +51,8 @@ internal class PeerConnectionManager
private val coroutineScope: CoroutineScope =
ClosableCoroutineScope(SupervisorJob())
+ private var streamIds: List = listOf(UUID.randomUUID().toString())
+
private fun getSendEncodingsFromConfig(simulcastConfig: SimulcastConfig): List {
val sendEncodings = Constants.simulcastEncodings()
simulcastConfig.activeEncodings.forEach {
@@ -59,7 +61,11 @@ internal class PeerConnectionManager
return sendEncodings
}
- suspend fun addTrack(track: LocalTrack, streamIds: List) {
+ suspend fun addTrack(track: LocalTrack) {
+ addTrack(track, streamIds)
+ }
+
+ private suspend fun addTrack(track: LocalTrack, streamIds: List) {
val videoParameters =
(track as? LocalVideoTrack)?.videoParameters ?: (track as? LocalScreencastTrack)?.videoParameters
@@ -208,8 +214,6 @@ internal class PeerConnectionManager
this@PeerConnectionManager.peerConnection = pc
}
- val streamIds = listOf(UUID.randomUUID().toString())
-
localTracks.forEach {
addTrack(it, streamIds)
}
@@ -379,7 +383,10 @@ internal class PeerConnectionManager
}
val params = sender.parameters
val encoding = params?.encodings?.find { it.rid == trackEncoding.rid } ?: run {
- Timber.e("setTrackEncoding: Invalid encoding $trackEncoding, no such encoding found in peer connection")
+ Timber.e(
+ "setTrackEncoding: Invalid encoding $trackEncoding," +
+ "no such encoding found in peer connection"
+ )
return
}
encoding.active = enabled
diff --git a/MembraneRTC/src/main/java/org/membraneframework/rtc/RTCEngineCommunication.kt b/MembraneRTC/src/main/java/org/membraneframework/rtc/RTCEngineCommunication.kt
index 3c8a1e7..10f18d7 100644
--- a/MembraneRTC/src/main/java/org/membraneframework/rtc/RTCEngineCommunication.kt
+++ b/MembraneRTC/src/main/java/org/membraneframework/rtc/RTCEngineCommunication.kt
@@ -1,54 +1,43 @@
package org.membraneframework.rtc
+import com.google.gson.reflect.TypeToken
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import org.membraneframework.rtc.events.*
-import org.membraneframework.rtc.transport.EventTransport
-import org.membraneframework.rtc.transport.EventTransportError
-import org.membraneframework.rtc.transport.EventTransportListener
+import org.membraneframework.rtc.models.Endpoint
import org.membraneframework.rtc.utils.Metadata
+import org.membraneframework.rtc.utils.SerializedMediaEvent
import timber.log.Timber
import kotlin.math.roundToLong
internal class RTCEngineCommunication
@AssistedInject
constructor(
- @Assisted
- private val transport: EventTransport,
@Assisted
private val engineListener: RTCEngineListener
-) : EventTransportListener {
+) {
@AssistedFactory
interface RTCEngineCommunicationFactory {
fun create(
- transport: EventTransport,
listener: RTCEngineListener
): RTCEngineCommunication
}
- suspend fun connect() {
- transport.connect(this@RTCEngineCommunication)
- }
-
- suspend fun disconnect() {
- transport.disconnect()
+ fun connect(endpointMetadata: Metadata) {
+ sendEvent(Connect(endpointMetadata))
}
- suspend fun join(peerMetadata: Metadata) {
- transport.send(Join(peerMetadata))
+ fun updateEndpointMetadata(endpointMetadata: Metadata) {
+ sendEvent(UpdateEndpointMetadata(endpointMetadata))
}
- suspend fun updatePeerMetadata(peerMetadata: Metadata) {
- transport.send(UpdatePeerMetadata(peerMetadata))
+ fun updateTrackMetadata(trackId: String, trackMetadata: Metadata) {
+ sendEvent(UpdateTrackMetadata(trackId, trackMetadata))
}
- suspend fun updateTrackMetadata(trackId: String, trackMetadata: Metadata) {
- transport.send(UpdateTrackMetadata(trackId, trackMetadata))
- }
-
- suspend fun setTargetTrackEncoding(trackId: String, encoding: TrackEncoding) {
- transport.send(
+ fun setTargetTrackEncoding(trackId: String, encoding: TrackEncoding) {
+ sendEvent(
SelectEncoding(
trackId,
encoding.rid
@@ -56,12 +45,12 @@ constructor(
)
}
- suspend fun renegotiateTracks() {
- transport.send(RenegotiateTracks())
+ fun renegotiateTracks() {
+ sendEvent(RenegotiateTracks())
}
- suspend fun localCandidate(sdp: String, sdpMLineIndex: Int) {
- transport.send(
+ fun localCandidate(sdp: String, sdpMLineIndex: Int) {
+ sendEvent(
LocalCandidate(
sdp,
sdpMLineIndex
@@ -69,12 +58,12 @@ constructor(
)
}
- suspend fun sdpOffer(
+ fun sdpOffer(
sdp: String,
trackIdToTrackMetadata: Map,
midToTrackId: Map
) {
- transport.send(
+ sendEvent(
SdpOffer(
sdp,
trackIdToTrackMetadata,
@@ -83,26 +72,52 @@ constructor(
)
}
- override fun onEvent(event: ReceivableEvent) {
- when (event) {
+ fun disconnect() {
+ sendEvent(Disconnect())
+ }
+
+ private fun sendEvent(event: SendableEvent) {
+ val serializedMediaEvent = gson.toJson(event.serializeToMap())
+ engineListener.onSendMediaEvent(serializedMediaEvent)
+ }
+
+ private fun decodeEvent(event: SerializedMediaEvent): ReceivableEvent? {
+ val type = object : TypeToken