VoIP: remove dependency over WebRtc on SDK

This commit is contained in:
ganfra 2020-11-20 12:19:30 +01:00
parent f960cf2ce9
commit be3bfe7e5e
16 changed files with 157 additions and 94 deletions

View file

@ -178,12 +178,6 @@ dependencies {
// Phone number https://github.com/google/libphonenumber // Phone number https://github.com/google/libphonenumber
implementation 'com.googlecode.libphonenumber:libphonenumber:8.10.23' implementation 'com.googlecode.libphonenumber:libphonenumber:8.10.23'
// Web RTC
// org.webrtc:google-webrtc is for development purposes only. See http://webrtc.github.io/webrtc-org/native-code/android/
// implementation 'org.webrtc:google-webrtc:1.0.+'
// Use the same WebRTC library than the one used by Jitsi library
implementation('com.facebook.react:react-native-webrtc:1.84.0-jitsi-5112273@aar')
testImplementation 'junit:junit:4.13' testImplementation 'junit:junit:4.13'
testImplementation 'org.robolectric:robolectric:4.3' testImplementation 'org.robolectric:robolectric:4.3'
//testImplementation 'org.robolectric:shadows-support-v4:3.0' //testImplementation 'org.robolectric:shadows-support-v4:3.0'

View file

@ -16,7 +16,6 @@
package org.matrix.android.sdk.api.session.call package org.matrix.android.sdk.api.session.call
import org.webrtc.PeerConnection
sealed class CallState { sealed class CallState {
@ -42,7 +41,7 @@ sealed class CallState {
* Notice that the PeerState failed is not always final, if you switch network, new ice candidtates * Notice that the PeerState failed is not always final, if you switch network, new ice candidtates
* could be exchanged, and the connection could go back to connected * could be exchanged, and the connection could go back to connected
* */ * */
data class Connected(val iceConnectionState: PeerConnection.PeerConnectionState) : CallState() data class Connected(val iceConnectionState: MxPeerConnectionState) : CallState()
/** Terminated. Incoming/Outgoing call, the call is terminated */ /** Terminated. Incoming/Outgoing call, the call is terminated */
object Terminated : CallState() object Terminated : CallState()

View file

@ -16,9 +16,8 @@
package org.matrix.android.sdk.api.session.call package org.matrix.android.sdk.api.session.call
import org.matrix.android.sdk.api.session.room.model.call.CallCandidate
import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.Optional
import org.webrtc.IceCandidate
import org.webrtc.SessionDescription
interface MxCallDetail { interface MxCallDetail {
val callId: String val callId: String
@ -47,12 +46,12 @@ interface MxCall : MxCallDetail {
* Pick Up the incoming call * Pick Up the incoming call
* It has no effect on outgoing call * It has no effect on outgoing call
*/ */
fun accept(sdp: SessionDescription) fun accept(sdpString: String)
/** /**
* SDP negotiation for media pause, hold/resume, ICE restarts and voice/video call up/downgrading * SDP negotiation for media pause, hold/resume, ICE restarts and voice/video call up/downgrading
*/ */
fun negotiate(sdp: SessionDescription) fun negotiate(sdpString: String)
/** /**
* This has to be sent by the caller's client once it has chosen an answer. * This has to be sent by the caller's client once it has chosen an answer.
@ -73,17 +72,17 @@ interface MxCall : MxCallDetail {
* Start a call * Start a call
* Send offer SDP to the other participant. * Send offer SDP to the other participant.
*/ */
fun offerSdp(sdp: SessionDescription) fun offerSdp(sdpString: String)
/** /**
* Send Ice candidate to the other participant. * Send Ice candidate to the other participant.
*/ */
fun sendLocalIceCandidates(candidates: List<IceCandidate>) fun sendLocalIceCandidates(candidates: List<CallCandidate>)
/** /**
* Send removed ICE candidates to the other participant. * Send removed ICE candidates to the other participant.
*/ */
fun sendLocalIceCandidateRemovals(candidates: List<IceCandidate>) fun sendLocalIceCandidateRemovals(candidates: List<CallCandidate>)
fun addListener(listener: StateListener) fun addListener(listener: StateListener)
fun removeListener(listener: StateListener) fun removeListener(listener: StateListener)

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.session.call;
/**
* This is a copy of https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionState
* to avoid having the dependency over WebRtc library on sdk.
*/
public enum MxPeerConnectionState {
NEW,
CONNECTING,
CONNECTED,
DISCONNECTED,
FAILED,
CLOSED;
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.session.room.model.call
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class CallCandidate(
/**
* Required. The SDP media type this candidate is intended for.
*/
@Json(name = "sdpMid") val sdpMid: String,
/**
* Required. The index of the SDP 'm' line this candidate is intended for.
*/
@Json(name = "sdpMLineIndex") val sdpMLineIndex: Int,
/**
* Required. The SDP 'a' line of the candidate.
*/
@Json(name = "candidate") val candidate: String
)

View file

@ -36,26 +36,9 @@ data class CallCandidatesContent(
/** /**
* Required. Array of objects describing the candidates. * Required. Array of objects describing the candidates.
*/ */
@Json(name = "candidates") val candidates: List<Candidate> = emptyList(), @Json(name = "candidates") val candidates: List<CallCandidate> = emptyList(),
/** /**
* Required. The version of the VoIP specification this messages adheres to. This specification is version 0. * Required. The version of the VoIP specification this messages adheres to. This specification is version 0.
*/ */
@Json(name = "version") override val version: String? = "0" @Json(name = "version") override val version: String? = "0"
): CallSignallingContent { ): CallSignallingContent
@JsonClass(generateAdapter = true)
data class Candidate(
/**
* Required. The SDP media type this candidate is intended for.
*/
@Json(name = "sdpMid") val sdpMid: String,
/**
* Required. The index of the SDP 'm' line this candidate is intended for.
*/
@Json(name = "sdpMLineIndex") val sdpMLineIndex: Int,
/**
* Required. The SDP 'a' line of the candidate.
*/
@Json(name = "candidate") val candidate: String
)
}

View file

@ -18,7 +18,6 @@ package org.matrix.android.sdk.api.session.room.model.call
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import org.webrtc.SessionDescription
@JsonClass(generateAdapter = false) @JsonClass(generateAdapter = false)
enum class SdpType { enum class SdpType {
@ -28,19 +27,3 @@ enum class SdpType {
@Json(name = "answer") @Json(name = "answer")
ANSWER; ANSWER;
} }
fun SdpType.asWebRTC(): SessionDescription.Type {
return if (this == SdpType.OFFER) {
SessionDescription.Type.OFFER
} else {
SessionDescription.Type.ANSWER
}
}
fun SessionDescription.Type.toSdpType(): SdpType {
return if (this == SessionDescription.Type.OFFER) {
SdpType.OFFER
} else {
SdpType.ANSWER
}
}

View file

@ -25,19 +25,18 @@ import org.matrix.android.sdk.api.session.events.model.LocalEcho
import org.matrix.android.sdk.api.session.events.model.UnsignedData import org.matrix.android.sdk.api.session.events.model.UnsignedData
import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent
import org.matrix.android.sdk.api.session.room.model.call.CallCandidate
import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent
import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent
import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
import org.matrix.android.sdk.api.session.room.model.call.CallNegotiateContent import org.matrix.android.sdk.api.session.room.model.call.CallNegotiateContent
import org.matrix.android.sdk.api.session.room.model.call.CallRejectContent import org.matrix.android.sdk.api.session.room.model.call.CallRejectContent
import org.matrix.android.sdk.api.session.room.model.call.CallSelectAnswerContent import org.matrix.android.sdk.api.session.room.model.call.CallSelectAnswerContent
import org.matrix.android.sdk.api.session.room.model.call.toSdpType import org.matrix.android.sdk.api.session.room.model.call.SdpType
import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.internal.session.call.DefaultCallSignalingService import org.matrix.android.sdk.internal.session.call.DefaultCallSignalingService
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
import org.webrtc.IceCandidate
import org.webrtc.SessionDescription
import timber.log.Timber import timber.log.Timber
internal class MxCallImpl( internal class MxCallImpl(
@ -90,7 +89,7 @@ internal class MxCallImpl(
} }
} }
override fun offerSdp(sdp: SessionDescription) { override fun offerSdp(sdpString: String) {
if (!isOutgoing) return if (!isOutgoing) return
Timber.v("## VOIP offerSdp $callId") Timber.v("## VOIP offerSdp $callId")
state = CallState.Dialing state = CallState.Dialing
@ -98,29 +97,23 @@ internal class MxCallImpl(
callId = callId, callId = callId,
partyId = ourPartyId, partyId = ourPartyId,
lifetime = DefaultCallSignalingService.CALL_TIMEOUT_MS, lifetime = DefaultCallSignalingService.CALL_TIMEOUT_MS,
offer = CallInviteContent.Offer(sdp = sdp.description) offer = CallInviteContent.Offer(sdp = sdpString)
) )
.let { createEventAndLocalEcho(type = EventType.CALL_INVITE, roomId = roomId, content = it.toContent()) } .let { createEventAndLocalEcho(type = EventType.CALL_INVITE, roomId = roomId, content = it.toContent()) }
.also { eventSenderProcessor.postEvent(it) } .also { eventSenderProcessor.postEvent(it) }
} }
override fun sendLocalIceCandidates(candidates: List<IceCandidate>) { override fun sendLocalIceCandidates(candidates: List<CallCandidate>) {
CallCandidatesContent( CallCandidatesContent(
callId = callId, callId = callId,
partyId = ourPartyId, partyId = ourPartyId,
candidates = candidates.map { candidates = candidates
CallCandidatesContent.Candidate(
sdpMid = it.sdpMid,
sdpMLineIndex = it.sdpMLineIndex,
candidate = it.sdp
)
}
) )
.let { createEventAndLocalEcho(type = EventType.CALL_CANDIDATES, roomId = roomId, content = it.toContent()) } .let { createEventAndLocalEcho(type = EventType.CALL_CANDIDATES, roomId = roomId, content = it.toContent()) }
.also { eventSenderProcessor.postEvent(it) } .also { eventSenderProcessor.postEvent(it) }
} }
override fun sendLocalIceCandidateRemovals(candidates: List<IceCandidate>) { override fun sendLocalIceCandidateRemovals(candidates: List<CallCandidate>) {
// For now we don't support this flow // For now we don't support this flow
} }
@ -153,26 +146,26 @@ internal class MxCallImpl(
state = CallState.Terminated state = CallState.Terminated
} }
override fun accept(sdp: SessionDescription) { override fun accept(sdpString: String) {
Timber.v("## VOIP accept $callId") Timber.v("## VOIP accept $callId")
if (isOutgoing) return if (isOutgoing) return
state = CallState.Answering state = CallState.Answering
CallAnswerContent( CallAnswerContent(
callId = callId, callId = callId,
partyId = ourPartyId, partyId = ourPartyId,
answer = CallAnswerContent.Answer(sdp = sdp.description) answer = CallAnswerContent.Answer(sdp = sdpString)
) )
.let { createEventAndLocalEcho(type = EventType.CALL_ANSWER, roomId = roomId, content = it.toContent()) } .let { createEventAndLocalEcho(type = EventType.CALL_ANSWER, roomId = roomId, content = it.toContent()) }
.also { eventSenderProcessor.postEvent(it) } .also { eventSenderProcessor.postEvent(it) }
} }
override fun negotiate(sdp: SessionDescription) { override fun negotiate(sdpString: String) {
Timber.v("## VOIP negotiate $callId") Timber.v("## VOIP negotiate $callId")
CallNegotiateContent( CallNegotiateContent(
callId = callId, callId = callId,
partyId = ourPartyId, partyId = ourPartyId,
lifetime = DefaultCallSignalingService.CALL_TIMEOUT_MS, lifetime = DefaultCallSignalingService.CALL_TIMEOUT_MS,
description = CallNegotiateContent.Description(sdp = sdp.description, type = sdp.type.toSdpType()) description = CallNegotiateContent.Description(sdp = sdpString, type = SdpType.OFFER)
) )
.let { createEventAndLocalEcho(type = EventType.CALL_NEGOTIATE, roomId = roomId, content = it.toContent()) } .let { createEventAndLocalEcho(type = EventType.CALL_NEGOTIATE, roomId = roomId, content = it.toContent()) }
.also { eventSenderProcessor.postEvent(it) } .also { eventSenderProcessor.postEvent(it) }

View file

@ -22,7 +22,7 @@ import androidx.core.view.isVisible
import im.vector.app.core.utils.DebouncedClickListener import im.vector.app.core.utils.DebouncedClickListener
import im.vector.app.features.call.WebRtcPeerConnectionManager import im.vector.app.features.call.WebRtcPeerConnectionManager
import org.matrix.android.sdk.api.session.call.CallState import org.matrix.android.sdk.api.session.call.CallState
import org.matrix.android.sdk.api.session.call.EglUtils import im.vector.app.features.call.utils.EglUtils
import org.matrix.android.sdk.api.session.call.MxCall import org.matrix.android.sdk.api.session.call.MxCall
import org.webrtc.RendererCommon import org.webrtc.RendererCommon
import org.webrtc.SurfaceViewRenderer import org.webrtc.SurfaceViewRenderer

View file

@ -29,6 +29,7 @@ import butterknife.OnClick
import im.vector.app.R import im.vector.app.R
import kotlinx.android.synthetic.main.view_call_controls.view.* import kotlinx.android.synthetic.main.view_call_controls.view.*
import org.matrix.android.sdk.api.session.call.CallState import org.matrix.android.sdk.api.session.call.CallState
import org.matrix.android.sdk.api.session.call.MxPeerConnectionState
import org.webrtc.PeerConnection import org.webrtc.PeerConnection
class CallControlsView @JvmOverloads constructor( class CallControlsView @JvmOverloads constructor(
@ -129,7 +130,7 @@ class CallControlsView @JvmOverloads constructor(
connectedControls.isVisible = false connectedControls.isVisible = false
} }
is CallState.Connected -> { is CallState.Connected -> {
if (callState.iceConnectionState == PeerConnection.PeerConnectionState.CONNECTED) { if (callState.iceConnectionState == MxPeerConnectionState.CONNECTED) {
ringingControls.isVisible = false ringingControls.isVisible = false
connectedControls.isVisible = true connectedControls.isVisible = true
iv_video_toggle.isVisible = state.isVideoCall iv_video_toggle.isVisible = state.isVideoCall

View file

@ -52,8 +52,9 @@ import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.activity_call.* import kotlinx.android.synthetic.main.activity_call.*
import org.matrix.android.sdk.api.session.call.CallState import org.matrix.android.sdk.api.session.call.CallState
import org.matrix.android.sdk.api.session.call.EglUtils import im.vector.app.features.call.utils.EglUtils
import org.matrix.android.sdk.api.session.call.MxCallDetail import org.matrix.android.sdk.api.session.call.MxCallDetail
import org.matrix.android.sdk.api.session.call.MxPeerConnectionState
import org.matrix.android.sdk.api.session.call.TurnServerResponse import org.matrix.android.sdk.api.session.call.TurnServerResponse
import org.webrtc.EglBase import org.webrtc.EglBase
import org.webrtc.PeerConnection import org.webrtc.PeerConnection
@ -255,7 +256,7 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
configureCallInfo(state) configureCallInfo(state)
} }
is CallState.Connected -> { is CallState.Connected -> {
if (callState.iceConnectionState == PeerConnection.PeerConnectionState.CONNECTED) { if (callState.iceConnectionState == MxPeerConnectionState.CONNECTED) {
if (callArgs.isVideoCall) { if (callArgs.isVideoCall) {
callVideoGroup.isVisible = true callVideoGroup.isVisible = true
callInfoGroup.isVisible = false callInfoGroup.isVisible = false

View file

@ -30,6 +30,7 @@ import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.call.CallState import org.matrix.android.sdk.api.session.call.CallState
import org.matrix.android.sdk.api.session.call.MxCall import org.matrix.android.sdk.api.session.call.MxCall
import org.matrix.android.sdk.api.session.call.MxPeerConnectionState
import org.matrix.android.sdk.api.session.call.TurnServerResponse import org.matrix.android.sdk.api.session.call.TurnServerResponse
import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.MatrixItem
import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.api.util.toMatrixItem
@ -53,7 +54,7 @@ class VectorCallViewModel @AssistedInject constructor(
private val callStateListener = object : MxCall.StateListener { private val callStateListener = object : MxCall.StateListener {
override fun onStateUpdate(call: MxCall) { override fun onStateUpdate(call: MxCall) {
val callState = call.state val callState = call.state
if (callState is CallState.Connected && callState.iceConnectionState == PeerConnection.PeerConnectionState.CONNECTED) { if (callState is CallState.Connected && callState.iceConnectionState == MxPeerConnectionState.CONNECTED) {
hasBeenConnectedOnce = true hasBeenConnectedOnce = true
connectionTimeoutTimer?.cancel() connectionTimeoutTimer?.cancel()
connectionTimeoutTimer = null connectionTimeoutTimer = null

View file

@ -46,8 +46,11 @@ import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.call.CallListener import org.matrix.android.sdk.api.session.call.CallListener
import org.matrix.android.sdk.api.session.call.CallState import org.matrix.android.sdk.api.session.call.CallState
import org.matrix.android.sdk.api.session.call.EglUtils import im.vector.app.features.call.utils.EglUtils
import im.vector.app.features.call.utils.asWebRTC
import im.vector.app.features.call.utils.mapToCallCandidate
import org.matrix.android.sdk.api.session.call.MxCall import org.matrix.android.sdk.api.session.call.MxCall
import org.matrix.android.sdk.api.session.call.MxPeerConnectionState
import org.matrix.android.sdk.api.session.call.TurnServerResponse import org.matrix.android.sdk.api.session.call.TurnServerResponse
import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent
import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent
@ -57,7 +60,6 @@ import org.matrix.android.sdk.api.session.room.model.call.CallNegotiateContent
import org.matrix.android.sdk.api.session.room.model.call.CallRejectContent import org.matrix.android.sdk.api.session.room.model.call.CallRejectContent
import org.matrix.android.sdk.api.session.room.model.call.CallSelectAnswerContent import org.matrix.android.sdk.api.session.room.model.call.CallSelectAnswerContent
import org.matrix.android.sdk.api.session.room.model.call.SdpType import org.matrix.android.sdk.api.session.room.model.call.SdpType
import org.matrix.android.sdk.api.session.room.model.call.asWebRTC
import org.matrix.android.sdk.internal.util.awaitCallback import org.matrix.android.sdk.internal.util.awaitCallback
import org.webrtc.AudioSource import org.webrtc.AudioSource
import org.webrtc.AudioTrack import org.webrtc.AudioTrack
@ -150,7 +152,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
if (it.isNotEmpty()) { if (it.isNotEmpty()) {
Timber.v("## Sending local ice candidates to call") Timber.v("## Sending local ice candidates to call")
// it.forEach { peerConnection?.addIceCandidate(it) } // it.forEach { peerConnection?.addIceCandidate(it) }
mxCall.sendLocalIceCandidates(it) mxCall.sendLocalIceCandidates(it.mapToCallCandidate())
} }
} }
@ -329,9 +331,9 @@ class WebRtcPeerConnectionManager @Inject constructor(
} }
if (call.state == CallState.CreateOffer) { if (call.state == CallState.CreateOffer) {
// send offer to peer // send offer to peer
call.offerSdp(sessionDescription) call.offerSdp(sessionDescription.description)
} else { } else {
call.negotiate(sessionDescription) call.negotiate(sessionDescription.description)
} }
} catch (failure: Throwable) { } catch (failure: Throwable) {
// Need to handle error properly. // Need to handle error properly.
@ -455,7 +457,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
// create a answer, set local description and send via signaling // create a answer, set local description and send via signaling
createAnswer(callContext)?.also { createAnswer(callContext)?.also {
callContext.mxCall.accept(it) callContext.mxCall.accept(it.description)
} }
Timber.v("## VOIP remoteCandidateSource ${callContext.remoteCandidateSource}") Timber.v("## VOIP remoteCandidateSource ${callContext.remoteCandidateSource}")
callContext.remoteIceCandidateDisposable = callContext.remoteCandidateSource?.subscribe({ callContext.remoteIceCandidateDisposable = callContext.remoteCandidateSource?.subscribe({
@ -969,7 +971,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
peerConnection.awaitSetRemoteDescription(sdp) peerConnection.awaitSetRemoteDescription(sdp)
if (type == SdpType.OFFER) { if (type == SdpType.OFFER) {
createAnswer(call)?.also { createAnswer(call)?.also {
call.mxCall.negotiate(it) call.mxCall.negotiate(sdpText)
} }
} }
} catch (failure: Throwable) { } catch (failure: Throwable) {
@ -1053,7 +1055,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
* or is closed (state "closed"); in addition, at least one transport is either "connected" or "completed" * or is closed (state "closed"); in addition, at least one transport is either "connected" or "completed"
*/ */
PeerConnection.PeerConnectionState.CONNECTED -> { PeerConnection.PeerConnectionState.CONNECTED -> {
callContext.mxCall.state = CallState.Connected(newState) callContext.mxCall.state = CallState.Connected(MxPeerConnectionState.CONNECTED)
callAudioManager.onCallConnected(callContext.mxCall) callAudioManager.onCallConnected(callContext.mxCall)
} }
/** /**
@ -1062,7 +1064,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
PeerConnection.PeerConnectionState.FAILED -> { PeerConnection.PeerConnectionState.FAILED -> {
// This can be temporary, e.g when other ice not yet received... // This can be temporary, e.g when other ice not yet received...
// callContext.mxCall.state = CallState.ERROR // callContext.mxCall.state = CallState.ERROR
callContext.mxCall.state = CallState.Connected(newState) callContext.mxCall.state = CallState.Connected(MxPeerConnectionState.FAILED)
} }
/** /**
* At least one of the connection's ICE transports (RTCIceTransports or RTCDtlsTransports) are in the "new" state, * At least one of the connection's ICE transports (RTCIceTransports or RTCDtlsTransports) are in the "new" state,
@ -1070,26 +1072,27 @@ class WebRtcPeerConnectionManager @Inject constructor(
* or all of the connection's transports are in the "closed" state. * or all of the connection's transports are in the "closed" state.
*/ */
PeerConnection.PeerConnectionState.NEW, PeerConnection.PeerConnectionState.NEW,
/** /**
* One or more of the ICE transports are currently in the process of establishing a connection; * One or more of the ICE transports are currently in the process of establishing a connection;
* that is, their RTCIceConnectionState is either "checking" or "connected", and no transports are in the "failed" state * that is, their RTCIceConnectionState is either "checking" or "connected", and no transports are in the "failed" state
*/ */
PeerConnection.PeerConnectionState.CONNECTING -> { PeerConnection.PeerConnectionState.CONNECTING -> {
callContext.mxCall.state = CallState.Connected(PeerConnection.PeerConnectionState.CONNECTING) callContext.mxCall.state = CallState.Connected(MxPeerConnectionState.CONNECTING)
} }
/** /**
* The RTCPeerConnection is closed. * The RTCPeerConnection is closed.
* This value was in the RTCSignalingState enum (and therefore found by reading the value of the signalingState) * This value was in the RTCSignalingState enum (and therefore found by reading the value of the signalingState)
* property until the May 13, 2016 draft of the specification. * property until the May 13, 2016 draft of the specification.
*/ */
PeerConnection.PeerConnectionState.CLOSED, PeerConnection.PeerConnectionState.CLOSED -> {
/** callContext.mxCall.state = CallState.Connected(MxPeerConnectionState.CLOSED)
* At least one of the ICE transports for the connection is in the "disconnected" state and none of }
* the other transports are in the state "failed", "connecting", or "checking". /**
*/ * At least one of the ICE transports for the connection is in the "disconnected" state and none of
* the other transports are in the state "failed", "connecting", or "checking".
*/
PeerConnection.PeerConnectionState.DISCONNECTED -> { PeerConnection.PeerConnectionState.DISCONNECTED -> {
callContext.mxCall.state = CallState.Connected(newState) callContext.mxCall.state = CallState.Connected(MxPeerConnectionState.DISCONNECTED)
} }
null -> { null -> {
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright 2020 The Matrix.org Foundation C.I.C. * Copyright (c) 2020 New Vector Ltd
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.session.call package im.vector.app.features.call.utils
import org.webrtc.EglBase import org.webrtc.EglBase
import timber.log.Timber import timber.log.Timber

View file

@ -17,6 +17,7 @@
package im.vector.app.features.call.utils package im.vector.app.features.call.utils
import im.vector.app.features.call.SdpObserverAdapter import im.vector.app.features.call.SdpObserverAdapter
import org.matrix.android.sdk.api.session.call.MxPeerConnectionState
import org.webrtc.MediaConstraints import org.webrtc.MediaConstraints
import org.webrtc.PeerConnection import org.webrtc.PeerConnection
import org.webrtc.SessionDescription import org.webrtc.SessionDescription
@ -79,4 +80,3 @@ suspend fun PeerConnection.awaitSetRemoteDescription(sessionDescription: Session
} }
}, sessionDescription) }, sessionDescription)
} }

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.call.utils
import org.matrix.android.sdk.api.session.room.model.call.CallCandidate
import org.matrix.android.sdk.api.session.room.model.call.SdpType
import org.webrtc.IceCandidate
import org.webrtc.SessionDescription
fun List<IceCandidate>.mapToCallCandidate() = map {
CallCandidate(
sdpMid = it.sdpMid,
sdpMLineIndex = it.sdpMLineIndex,
candidate = it.sdp
)
}
fun SdpType.asWebRTC(): SessionDescription.Type {
return if (this == SdpType.OFFER) {
SessionDescription.Type.OFFER
} else {
SessionDescription.Type.ANSWER
}
}