Use single sdp and stream observer.

This commit is contained in:
onurays 2020-06-03 11:36:59 +03:00 committed by Valere
parent 4b85e39e3e
commit 79f804b2d4

View file

@ -22,11 +22,13 @@ import android.content.Intent
import android.content.ServiceConnection import android.content.ServiceConnection
import android.os.IBinder import android.os.IBinder
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.extensions.tryThis import im.vector.matrix.android.api.extensions.tryThis
import im.vector.matrix.android.api.session.call.CallsListener import im.vector.matrix.android.api.session.call.CallsListener
import im.vector.matrix.android.api.session.call.EglUtils import im.vector.matrix.android.api.session.call.EglUtils
import im.vector.matrix.android.api.session.call.MxCall import im.vector.matrix.android.api.session.call.MxCall
import im.vector.matrix.android.api.session.call.MxCallDetail import im.vector.matrix.android.api.session.call.MxCallDetail
import im.vector.matrix.android.api.session.call.TurnServer
import im.vector.matrix.android.api.session.room.model.call.CallAnswerContent import im.vector.matrix.android.api.session.room.model.call.CallAnswerContent
import im.vector.matrix.android.api.session.room.model.call.CallHangupContent import im.vector.matrix.android.api.session.room.model.call.CallHangupContent
import im.vector.matrix.android.api.session.room.model.call.CallInviteContent import im.vector.matrix.android.api.session.room.model.call.CallInviteContent
@ -38,6 +40,7 @@ import org.webrtc.AudioSource
import org.webrtc.AudioTrack import org.webrtc.AudioTrack
import org.webrtc.Camera1Enumerator import org.webrtc.Camera1Enumerator
import org.webrtc.Camera2Enumerator import org.webrtc.Camera2Enumerator
import org.webrtc.DataChannel
import org.webrtc.DefaultVideoDecoderFactory import org.webrtc.DefaultVideoDecoderFactory
import org.webrtc.DefaultVideoEncoderFactory import org.webrtc.DefaultVideoEncoderFactory
import org.webrtc.IceCandidate import org.webrtc.IceCandidate
@ -45,7 +48,7 @@ import org.webrtc.MediaConstraints
import org.webrtc.MediaStream import org.webrtc.MediaStream
import org.webrtc.PeerConnection import org.webrtc.PeerConnection
import org.webrtc.PeerConnectionFactory import org.webrtc.PeerConnectionFactory
import org.webrtc.SdpObserver import org.webrtc.RtpReceiver
import org.webrtc.SessionDescription import org.webrtc.SessionDescription
import org.webrtc.SurfaceTextureHelper import org.webrtc.SurfaceTextureHelper
import org.webrtc.SurfaceViewRenderer import org.webrtc.SurfaceViewRenderer
@ -71,18 +74,17 @@ class WebRtcPeerConnectionManager @Inject constructor(
var localMediaStream: MediaStream? = null var localMediaStream: MediaStream? = null
// *Comments copied from webrtc demo app*
// Executor thread is started once and is used for all
// peer connection API calls to ensure new peer connection factory is
// created on the same thread as previously destroyed factory.
private val executor = Executors.newSingleThreadExecutor() private val executor = Executors.newSingleThreadExecutor()
private val rootEglBase by lazy { EglUtils.rootEglBase } private val rootEglBase by lazy { EglUtils.rootEglBase }
private var peerConnectionFactory: PeerConnectionFactory? = null private var peerConnectionFactory: PeerConnectionFactory? = null
private var peerConnection: PeerConnection? = null private var peerConnection: PeerConnection? = null
private var localSdp: SessionDescription? = null
private var sdpObserver = SdpObserver()
private var streamObserver = StreamObserver()
private var localViewRenderer: SurfaceViewRenderer? = null private var localViewRenderer: SurfaceViewRenderer? = null
private var remoteViewRenderer: SurfaceViewRenderer? = null private var remoteViewRenderer: SurfaceViewRenderer? = null
@ -115,7 +117,6 @@ class WebRtcPeerConnectionManager @Inject constructor(
} }
private fun createPeerConnectionFactory() { private fun createPeerConnectionFactory() {
if (peerConnectionFactory == null) {
Timber.v("## VOIP createPeerConnectionFactory") Timber.v("## VOIP createPeerConnectionFactory")
val eglBaseContext = rootEglBase?.eglBaseContext ?: return Unit.also { val eglBaseContext = rootEglBase?.eglBaseContext ?: return Unit.also {
Timber.e("## VOIP No EGL BASE") Timber.e("## VOIP No EGL BASE")
@ -135,7 +136,6 @@ class WebRtcPeerConnectionManager @Inject constructor(
/* enableH264HighProfile */ /* enableH264HighProfile */
true) true)
val defaultVideoDecoderFactory = DefaultVideoDecoderFactory(eglBaseContext) val defaultVideoDecoderFactory = DefaultVideoDecoderFactory(eglBaseContext)
Timber.v("## VOIP PeerConnectionFactory.createPeerConnectionFactory ...") Timber.v("## VOIP PeerConnectionFactory.createPeerConnectionFactory ...")
peerConnectionFactory = PeerConnectionFactory.builder() peerConnectionFactory = PeerConnectionFactory.builder()
.setOptions(options) .setOptions(options)
@ -143,9 +143,23 @@ class WebRtcPeerConnectionManager @Inject constructor(
.setVideoDecoderFactory(defaultVideoDecoderFactory) .setVideoDecoderFactory(defaultVideoDecoderFactory)
.createPeerConnectionFactory() .createPeerConnectionFactory()
} }
}
private fun createPeerConnection(observer: PeerConnectionObserverAdapter) { private fun createPeerConnection(turnServer: TurnServer?) {
val iceServers = mutableListOf<PeerConnection.IceServer>().apply {
turnServer?.let { server ->
server.uris?.forEach { uri ->
add(
PeerConnection
.IceServer
.builder(uri)
.setUsername(server.username)
.setPassword(server.password)
.createIceServer()
)
}
}
}
/*
val iceServers = ArrayList<PeerConnection.IceServer>().apply { val iceServers = ArrayList<PeerConnection.IceServer>().apply {
listOf("turn:turn.matrix.org:3478?transport=udp", "turn:turn.matrix.org:3478?transport=tcp", "turns:turn.matrix.org:443?transport=tcp").forEach { listOf("turn:turn.matrix.org:3478?transport=udp", "turn:turn.matrix.org:3478?transport=tcp", "turns:turn.matrix.org:443?transport=tcp").forEach {
add( add(
@ -156,49 +170,12 @@ class WebRtcPeerConnectionManager @Inject constructor(
) )
} }
} }
*/
Timber.v("## VOIP creating peer connection... ") Timber.v("## VOIP creating peer connection... ")
peerConnection = peerConnectionFactory?.createPeerConnection(iceServers, observer) peerConnection = peerConnectionFactory?.createPeerConnection(iceServers, streamObserver)
} }
private fun startCall() { private fun startCall() {
createPeerConnectionFactory()
createPeerConnection(object : PeerConnectionObserverAdapter() {
override fun onIceCandidate(p0: IceCandidate?) {
Timber.v("## VOIP onIceCandidate local $p0")
p0?.let { iceCandidateSource.onNext(it) }
}
override fun onAddStream(mediaStream: MediaStream?) {
Timber.v("## VOIP onAddStream remote $mediaStream")
mediaStream?.videoTracks?.firstOrNull()?.let {
remoteVideoTrack = it
remoteSurfaceRenderer?.get()?.let { surface ->
it.setEnabled(true)
it.addSink(surface)
}
mediaStream.videoTracks?.firstOrNull()?.let { videoTrack ->
remoteVideoTrack = videoTrack
remoteVideoTrack?.setEnabled(true)
remoteViewRenderer?.let { remoteVideoTrack?.addSink(it) }
}
}
}
override fun onRemoveStream(mediaStream: MediaStream?) {
remoteSurfaceRenderer?.get()?.let {
remoteVideoTrack?.removeSink(it)
}
remoteVideoTrack = null
}
override fun onIceConnectionChange(p0: PeerConnection.IceConnectionState?) {
Timber.v("## VOIP onIceConnectionChange $p0")
if (p0 == PeerConnection.IceConnectionState.DISCONNECTED) {
endCall()
}
}
})
iceCandidateDisposable = iceCandidateSource iceCandidateDisposable = iceCandidateSource
.buffer(400, TimeUnit.MILLISECONDS) .buffer(400, TimeUnit.MILLISECONDS)
.subscribe { .subscribe {
@ -208,38 +185,26 @@ class WebRtcPeerConnectionManager @Inject constructor(
currentCall?.sendLocalIceCandidates(it) currentCall?.sendLocalIceCandidates(it)
} }
} }
executor.execute {
sessionHolder.getActiveSession().callService().getTurnServer(object : MatrixCallback<TurnServer?> {
override fun onSuccess(data: TurnServer?) {
createPeerConnectionFactory()
createPeerConnection(data)
}
})
}
} }
private fun sendSdpOffer() { private fun sendSdpOffer() {
executor.execute {
val constraints = MediaConstraints() val constraints = MediaConstraints()
constraints.mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true")) constraints.mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"))
constraints.mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveVideo", if (currentCall?.isVideoCall == true) "true" else "false")) constraints.mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveVideo", if (currentCall?.isVideoCall == true) "true" else "false"))
Timber.v("## VOIP creating offer...") Timber.v("## VOIP creating offer...")
peerConnection?.createOffer(object : SdpObserver { peerConnection?.createOffer(sdpObserver, constraints)
override fun onSetFailure(p0: String?) {
Timber.v("## VOIP onSetFailure $p0")
} }
override fun onSetSuccess() {
Timber.v("## VOIP onSetSuccess")
}
override fun onCreateSuccess(sessionDescription: SessionDescription) {
Timber.v("## VOIP onCreateSuccess $sessionDescription will set local description")
peerConnection?.setLocalDescription(object : SdpObserverAdapter() {
override fun onSetSuccess() {
Timber.v("## setLocalDescription success")
Timber.v("## sending offer")
currentCall?.offerSdp(sessionDescription)
}
}, sessionDescription)
}
override fun onCreateFailure(p0: String?) {
Timber.v("## VOIP onCreateFailure $p0")
}
}, constraints)
} }
fun attachViewRenderers(localViewRenderer: SurfaceViewRenderer, remoteViewRenderer: SurfaceViewRenderer) { fun attachViewRenderers(localViewRenderer: SurfaceViewRenderer, remoteViewRenderer: SurfaceViewRenderer) {
@ -289,8 +254,6 @@ class WebRtcPeerConnectionManager @Inject constructor(
Timber.v("## VOIP add local stream to peer connection") Timber.v("## VOIP add local stream to peer connection")
peerConnection?.addStream(localMediaStream) peerConnection?.addStream(localMediaStream)
startCall()
} }
fun detachRenderers() { fun detachRenderers() {
@ -367,8 +330,10 @@ class WebRtcPeerConnectionManager @Inject constructor(
startHeadsUpService(mxCall) startHeadsUpService(mxCall)
startCall() startCall()
executor.execute {
val sdp = SessionDescription(SessionDescription.Type.OFFER, callInviteContent.offer?.sdp) val sdp = SessionDescription(SessionDescription.Type.OFFER, callInviteContent.offer?.sdp)
peerConnection?.setRemoteDescription(object : SdpObserverAdapter() {}, sdp) peerConnection?.setRemoteDescription(sdpObserver, sdp)
}
} }
private fun startHeadsUpService(mxCall: MxCallDetail) { private fun startHeadsUpService(mxCall: MxCallDetail) {
@ -380,53 +345,17 @@ class WebRtcPeerConnectionManager @Inject constructor(
fun answerCall() { fun answerCall() {
if (currentCall != null) { if (currentCall != null) {
executor.execute { createAnswer() }
}
}
private fun createAnswer() {
val constraints = MediaConstraints().apply { val constraints = MediaConstraints().apply {
mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true")) mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"))
mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveVideo", if (currentCall?.isVideoCall == true) "true" else "false")) mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveVideo", if (currentCall?.isVideoCall == true) "true" else "false"))
} }
peerConnection?.createAnswer(object : SdpObserver { peerConnection?.createAnswer(sdpObserver, constraints)
override fun onCreateSuccess(sessionDescription: SessionDescription) {
Timber.v("## createAnswer onCreateSuccess")
val sdp = SessionDescription(sessionDescription.type, sessionDescription.description)
peerConnection?.setLocalDescription(object : SdpObserver {
override fun onSetSuccess() {
currentCall?.accept(sdp)
currentCall?.let {
context.startActivity(VectorCallActivity.newIntent(context, it))
}
}
override fun onCreateSuccess(localSdp: SessionDescription) {}
override fun onSetFailure(p0: String?) {
endCall()
}
override fun onCreateFailure(p0: String?) {
endCall()
}
}, sdp)
}
override fun onSetSuccess() {
Timber.v("## createAnswer onSetSuccess")
}
override fun onSetFailure(error: String) {
Timber.v("answerCall.onSetFailure failed: $error")
endCall()
}
override fun onCreateFailure(error: String) {
Timber.v("answerCall.onCreateFailure failed: $error")
endCall()
}
}, constraints)
}
} }
fun endCall() { fun endCall() {
@ -439,7 +368,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
executor.execute { executor.execute {
Timber.v("## answerReceived") Timber.v("## answerReceived")
val sdp = SessionDescription(SessionDescription.Type.ANSWER, callAnswerContent.answer.sdp) val sdp = SessionDescription(SessionDescription.Type.ANSWER, callAnswerContent.answer.sdp)
peerConnection?.setRemoteDescription(object : SdpObserverAdapter() {}, sdp) peerConnection?.setRemoteDescription(sdpObserver, sdp)
} }
} }
@ -447,4 +376,109 @@ class WebRtcPeerConnectionManager @Inject constructor(
currentCall = null currentCall = null
close() close()
} }
private inner class SdpObserver : org.webrtc.SdpObserver {
override fun onCreateSuccess(origSdp: SessionDescription) {
Timber.v("## VOIP SdpObserver onCreateSuccess")
if (localSdp != null) return
executor.execute {
localSdp = SessionDescription(origSdp.type, origSdp.description)
peerConnection?.setLocalDescription(sdpObserver, localSdp)
}
}
override fun onSetSuccess() {
Timber.v("## VOIP SdpObserver onSetSuccess")
executor.execute {
localSdp?.let {
if (currentCall?.isOutgoing == true) {
currentCall?.offerSdp(it)
} else {
currentCall?.accept(it)
currentCall?.let { context.startActivity(VectorCallActivity.newIntent(context, it)) }
}
}
}
}
override fun onCreateFailure(error: String) {
Timber.v("## VOIP SdpObserver onCreateFailure: $error")
}
override fun onSetFailure(error: String) {
Timber.v("## VOIP SdpObserver onSetFailure: $error")
}
}
private inner class StreamObserver : PeerConnection.Observer {
override fun onIceCandidate(iceCandidate: IceCandidate) {
Timber.v("## VOIP StreamObserver onIceCandidate: $iceCandidate")
iceCandidateSource.onNext(iceCandidate)
}
override fun onDataChannel(dc: DataChannel) {
Timber.v("## VOIP StreamObserver onDataChannel: ${dc.state()}")
}
override fun onIceConnectionReceivingChange(receiving: Boolean) {
Timber.v("## VOIP StreamObserver onIceConnectionReceivingChange: $receiving")
}
override fun onIceConnectionChange(newState: PeerConnection.IceConnectionState) {
when (newState) {
PeerConnection.IceConnectionState.CONNECTED -> Timber.v("## VOIP StreamObserver onIceConnectionChange.CONNECTED")
PeerConnection.IceConnectionState.DISCONNECTED -> {
Timber.v("## VOIP StreamObserver onIceConnectionChange.DISCONNECTED")
endCall()
}
PeerConnection.IceConnectionState.FAILED -> Timber.v("## VOIP StreamObserver onIceConnectionChange.FAILED")
else -> Timber.v("## VOIP StreamObserver onIceConnectionChange.$newState")
}
}
override fun onAddStream(stream: MediaStream) {
Timber.v("## VOIP StreamObserver onAddStream: $stream")
executor.execute {
if (stream.audioTracks.size > 1 || stream.videoTracks.size > 1) return@execute
if (stream.videoTracks.size == 1) {
remoteVideoTrack = stream.videoTracks.first()
remoteVideoTrack?.setEnabled(true)
remoteViewRenderer?.let { remoteVideoTrack?.addSink(it) }
}
}
}
override fun onRemoveStream(stream: MediaStream) {
Timber.v("## VOIP StreamObserver onRemoveStream")
executor.execute {
remoteSurfaceRenderer?.get()?.let {
remoteVideoTrack?.removeSink(it)
}
remoteVideoTrack = null
}
}
override fun onIceGatheringChange(newState: PeerConnection.IceGatheringState) {
Timber.v("## VOIP StreamObserver onIceGatheringChange: $newState")
}
override fun onSignalingChange(newState: PeerConnection.SignalingState) {
Timber.v("## VOIP StreamObserver onSignalingChange: $newState")
}
override fun onIceCandidatesRemoved(candidates: Array<out IceCandidate>) {
Timber.v("## VOIP StreamObserver onIceCandidatesRemoved: ${candidates.contentToString()}")
}
override fun onRenegotiationNeeded() {
Timber.v("## VOIP StreamObserver onRenegotiationNeeded")
}
override fun onAddTrack(p0: RtpReceiver?, p1: Array<out MediaStream>?) {
Timber.v("## VOIP StreamObserver onAddTrack")
}
}
} }