mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-29 14:38:45 +03:00
VoIP: remove dependency over WebRtc on SDK
This commit is contained in:
parent
f960cf2ce9
commit
be3bfe7e5e
16 changed files with 157 additions and 94 deletions
|
@ -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'
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
||||||
|
)
|
|
@ -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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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) }
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
* 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".
|
* 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 -> {
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue