mirror of
https://github.com/element-hq/element-android
synced 2024-11-25 02:45:37 +03:00
Implement rejecting incoming call.
This commit is contained in:
parent
743ace7e60
commit
f50f81d321
2 changed files with 111 additions and 100 deletions
|
@ -122,39 +122,37 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createPeerConnectionFactory() {
|
private fun createPeerConnectionFactory() {
|
||||||
executor.execute {
|
if (peerConnectionFactory == null) {
|
||||||
if (peerConnectionFactory == null) {
|
Timber.v("## VOIP createPeerConnectionFactory")
|
||||||
Timber.v("## VOIP createPeerConnectionFactory")
|
val eglBaseContext = rootEglBase?.eglBaseContext ?: return Unit.also {
|
||||||
val eglBaseContext = rootEglBase?.eglBaseContext ?: return@execute Unit.also {
|
Timber.e("## VOIP No EGL BASE")
|
||||||
Timber.e("## VOIP No EGL BASE")
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.v("## VOIP PeerConnectionFactory.initialize")
|
|
||||||
PeerConnectionFactory.initialize(PeerConnectionFactory
|
|
||||||
.InitializationOptions.builder(context.applicationContext)
|
|
||||||
.createInitializationOptions()
|
|
||||||
)
|
|
||||||
|
|
||||||
val options = PeerConnectionFactory.Options()
|
|
||||||
val defaultVideoEncoderFactory = DefaultVideoEncoderFactory(
|
|
||||||
eglBaseContext,
|
|
||||||
/* enableIntelVp8Encoder */
|
|
||||||
true,
|
|
||||||
/* enableH264HighProfile */
|
|
||||||
true)
|
|
||||||
val defaultVideoDecoderFactory = DefaultVideoDecoderFactory(eglBaseContext)
|
|
||||||
|
|
||||||
Timber.v("## VOIP PeerConnectionFactory.createPeerConnectionFactory ...")
|
|
||||||
peerConnectionFactory = PeerConnectionFactory.builder()
|
|
||||||
.setOptions(options)
|
|
||||||
.setVideoEncoderFactory(defaultVideoEncoderFactory)
|
|
||||||
.setVideoDecoderFactory(defaultVideoDecoderFactory)
|
|
||||||
.createPeerConnectionFactory()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Timber.v("## VOIP PeerConnectionFactory.initialize")
|
||||||
|
PeerConnectionFactory.initialize(PeerConnectionFactory
|
||||||
|
.InitializationOptions.builder(context.applicationContext)
|
||||||
|
.createInitializationOptions()
|
||||||
|
)
|
||||||
|
|
||||||
|
val options = PeerConnectionFactory.Options()
|
||||||
|
val defaultVideoEncoderFactory = DefaultVideoEncoderFactory(
|
||||||
|
eglBaseContext,
|
||||||
|
/* enableIntelVp8Encoder */
|
||||||
|
true,
|
||||||
|
/* enableH264HighProfile */
|
||||||
|
true)
|
||||||
|
val defaultVideoDecoderFactory = DefaultVideoDecoderFactory(eglBaseContext)
|
||||||
|
|
||||||
|
Timber.v("## VOIP PeerConnectionFactory.createPeerConnectionFactory ...")
|
||||||
|
peerConnectionFactory = PeerConnectionFactory.builder()
|
||||||
|
.setOptions(options)
|
||||||
|
.setVideoEncoderFactory(defaultVideoEncoderFactory)
|
||||||
|
.setVideoDecoderFactory(defaultVideoDecoderFactory)
|
||||||
|
.createPeerConnectionFactory()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createPeerConnection() {
|
private fun createPeerConnection(observer: PeerConnectionObserverAdapter) {
|
||||||
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(
|
||||||
|
@ -166,40 +164,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Timber.v("## VOIP creating peer connection... ")
|
Timber.v("## VOIP creating peer connection... ")
|
||||||
peerConnection = peerConnectionFactory?.createPeerConnection(
|
peerConnection = peerConnectionFactory?.createPeerConnection(iceServers, observer)
|
||||||
iceServers,
|
|
||||||
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 {
|
|
||||||
listener?.addRemoteVideoTrack(it)
|
|
||||||
remoteVideoTrack = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onRemoveStream(mediaStream: MediaStream?) {
|
|
||||||
mediaStream?.let {
|
|
||||||
listener?.removeRemoteVideoStream(it)
|
|
||||||
}
|
|
||||||
remoteSurfaceRenderer?.get()?.let {
|
|
||||||
remoteVideoTrack?.removeSink(it)
|
|
||||||
}
|
|
||||||
remoteVideoTrack = null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onIceConnectionChange(p0: PeerConnection.IceConnectionState?) {
|
|
||||||
Timber.v("## VOIP onIceConnectionChange $p0")
|
|
||||||
if (p0 == PeerConnection.IceConnectionState.DISCONNECTED) {
|
|
||||||
listener?.onDisconnect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO REMOVE THIS FUNCTION
|
// TODO REMOVE THIS FUNCTION
|
||||||
|
@ -244,8 +209,6 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||||
// }
|
// }
|
||||||
// .disposeOnDestroy()
|
// .disposeOnDestroy()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
localMediaStream = peerConnectionFactory?.createLocalMediaStream("ARDAMS") // magic value?
|
localMediaStream = peerConnectionFactory?.createLocalMediaStream("ARDAMS") // magic value?
|
||||||
localMediaStream?.addTrack(localVideoTrack)
|
localMediaStream?.addTrack(localVideoTrack)
|
||||||
localMediaStream?.addTrack(audioTrack)
|
localMediaStream?.addTrack(audioTrack)
|
||||||
|
@ -260,6 +223,8 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun answerReceived(callId: String, answerSdp: SessionDescription) {
|
fun answerReceived(callId: String, answerSdp: SessionDescription) {
|
||||||
|
this.callId = callId
|
||||||
|
|
||||||
executor.execute {
|
executor.execute {
|
||||||
Timber.v("## answerReceived $callId")
|
Timber.v("## answerReceived $callId")
|
||||||
peerConnection?.setRemoteDescription(object : SdpObserverAdapter() {}, answerSdp)
|
peerConnection?.setRemoteDescription(object : SdpObserverAdapter() {}, answerSdp)
|
||||||
|
@ -268,50 +233,84 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||||
|
|
||||||
private fun startCall() {
|
private fun startCall() {
|
||||||
createPeerConnectionFactory()
|
createPeerConnectionFactory()
|
||||||
createPeerConnection()
|
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 {
|
||||||
|
listener?.addRemoteVideoTrack(it)
|
||||||
|
remoteVideoTrack = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRemoveStream(mediaStream: MediaStream?) {
|
||||||
|
mediaStream?.let {
|
||||||
|
listener?.removeRemoteVideoStream(it)
|
||||||
|
}
|
||||||
|
remoteSurfaceRenderer?.get()?.let {
|
||||||
|
remoteVideoTrack?.removeSink(it)
|
||||||
|
}
|
||||||
|
remoteVideoTrack = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onIceConnectionChange(p0: PeerConnection.IceConnectionState?) {
|
||||||
|
Timber.v("## VOIP onIceConnectionChange $p0")
|
||||||
|
if (p0 == PeerConnection.IceConnectionState.DISCONNECTED) {
|
||||||
|
listener?.onDisconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
iceCandidateDisposable = iceCandidateSource
|
iceCandidateDisposable = iceCandidateSource
|
||||||
.buffer(400, TimeUnit.MILLISECONDS)
|
.buffer(400, TimeUnit.MILLISECONDS)
|
||||||
.subscribe {
|
.subscribe {
|
||||||
// omit empty :/
|
// omit empty :/
|
||||||
if (it.isNotEmpty()) {
|
if (it.isNotEmpty()) {
|
||||||
|
Timber.v("## Sending local ice candidates to callId: $callId roomId: $signalingRoomId")
|
||||||
sessionHolder
|
sessionHolder
|
||||||
.getActiveSession()
|
.getActiveSession()
|
||||||
.callService()
|
.callService()
|
||||||
.sendLocalIceCandidates(callId ?: "", signalingRoomId ?: "", it)
|
.sendLocalIceCandidates(callId ?: "", signalingRoomId ?: "", it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
executor.execute {
|
private fun sendSdpOffer() {
|
||||||
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", "true"))
|
constraints.mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"))
|
||||||
|
|
||||||
Timber.v("## VOIP creating offer...")
|
Timber.v("## VOIP creating offer...")
|
||||||
peerConnection?.createOffer(object : SdpObserver {
|
peerConnection?.createOffer(object : SdpObserver {
|
||||||
override fun onSetFailure(p0: String?) {
|
override fun onSetFailure(p0: String?) {
|
||||||
Timber.v("## VOIP onSetFailure $p0")
|
Timber.v("## VOIP onSetFailure $p0")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSetSuccess() {
|
override fun onSetSuccess() {
|
||||||
Timber.v("## VOIP onSetSuccess")
|
Timber.v("## VOIP onSetSuccess")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateSuccess(sessionDescription: SessionDescription) {
|
override fun onCreateSuccess(sessionDescription: SessionDescription) {
|
||||||
Timber.v("## VOIP onCreateSuccess $sessionDescription")
|
Timber.v("## VOIP onCreateSuccess $sessionDescription will set local description")
|
||||||
peerConnection?.setLocalDescription(object : SdpObserverAdapter() {
|
peerConnection?.setLocalDescription(object : SdpObserverAdapter() {
|
||||||
override fun onSetSuccess() {
|
override fun onSetSuccess() {
|
||||||
callId = UUID.randomUUID().toString()
|
Timber.v("## setLocalDescription success")
|
||||||
sessionHolder.getActiveSession().callService().sendOfferSdp(callId!!, signalingRoomId!!, sessionDescription, object : MatrixCallback<String> {})
|
callId = UUID.randomUUID().toString()
|
||||||
}
|
Timber.v("## sending offer to callId: $callId roomId: $signalingRoomId")
|
||||||
}, sessionDescription)
|
sessionHolder.getActiveSession().callService().sendOfferSdp(callId ?: "", signalingRoomId
|
||||||
}
|
?: "", sessionDescription, object : MatrixCallback<String> {})
|
||||||
|
}
|
||||||
|
}, sessionDescription)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateFailure(p0: String?) {
|
override fun onCreateFailure(p0: String?) {
|
||||||
Timber.v("## VOIP onCreateFailure $p0")
|
Timber.v("## VOIP onCreateFailure $p0")
|
||||||
}
|
}
|
||||||
}, constraints)
|
}, constraints)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun attachViewRenderers(localViewRenderer: SurfaceViewRenderer, remoteViewRenderer: SurfaceViewRenderer) {
|
fun attachViewRenderers(localViewRenderer: SurfaceViewRenderer, remoteViewRenderer: SurfaceViewRenderer) {
|
||||||
|
@ -339,7 +338,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||||
executor.execute {
|
executor.execute {
|
||||||
// Do not dispose peer connection (https://bugs.chromium.org/p/webrtc/issues/detail?id=7543)
|
// Do not dispose peer connection (https://bugs.chromium.org/p/webrtc/issues/detail?id=7543)
|
||||||
peerConnection?.close()
|
peerConnection?.close()
|
||||||
peerConnection?.removeStream(localMediaStream)
|
localMediaStream?.let { peerConnection?.removeStream(it) }
|
||||||
peerConnection = null
|
peerConnection = null
|
||||||
audioSource?.dispose()
|
audioSource?.dispose()
|
||||||
videoSource?.dispose()
|
videoSource?.dispose()
|
||||||
|
@ -377,13 +376,19 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||||
fun startOutgoingCall(context: Context, signalingRoomId: String, participantUserId: String, isVideoCall: Boolean) {
|
fun startOutgoingCall(context: Context, signalingRoomId: String, participantUserId: String, isVideoCall: Boolean) {
|
||||||
this.signalingRoomId = signalingRoomId
|
this.signalingRoomId = signalingRoomId
|
||||||
this.participantUserId = participantUserId
|
this.participantUserId = participantUserId
|
||||||
|
|
||||||
startHeadsUpService(signalingRoomId, sessionHolder.getActiveSession().myUserId, false, isVideoCall)
|
startHeadsUpService(signalingRoomId, sessionHolder.getActiveSession().myUserId, false, isVideoCall)
|
||||||
context.startActivity(VectorCallActivity.newIntent(context, signalingRoomId, participantUserId, false, isVideoCall))
|
context.startActivity(VectorCallActivity.newIntent(context, signalingRoomId, participantUserId, false, isVideoCall))
|
||||||
|
|
||||||
startCall()
|
startCall()
|
||||||
|
sendSdpOffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCallInviteReceived(signalingRoomId: String, participantUserId: String, callInviteContent: CallInviteContent) {
|
override fun onCallInviteReceived(signalingRoomId: String, participantUserId: String, callInviteContent: CallInviteContent) {
|
||||||
|
this.callId = callInviteContent.callId
|
||||||
|
this.signalingRoomId = signalingRoomId
|
||||||
|
this.participantUserId = participantUserId
|
||||||
|
|
||||||
startHeadsUpService(signalingRoomId, participantUserId, true, callInviteContent.isVideo())
|
startHeadsUpService(signalingRoomId, participantUserId, true, callInviteContent.isVideo())
|
||||||
|
|
||||||
startCall()
|
startCall()
|
||||||
|
@ -399,8 +404,8 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||||
fun endCall() {
|
fun endCall() {
|
||||||
if (callId != null && signalingRoomId != null) {
|
if (callId != null && signalingRoomId != null) {
|
||||||
sessionHolder.getActiveSession().callService().sendHangup(callId!!, signalingRoomId!!)
|
sessionHolder.getActiveSession().callService().sendHangup(callId!!, signalingRoomId!!)
|
||||||
close()
|
|
||||||
}
|
}
|
||||||
|
close()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCallAnswerReceived(callAnswerContent: CallAnswerContent) {
|
override fun onCallAnswerReceived(callAnswerContent: CallAnswerContent) {
|
||||||
|
|
|
@ -19,11 +19,22 @@ package im.vector.riotx.features.call.service
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import im.vector.riotx.core.di.HasVectorInjector
|
||||||
|
import im.vector.riotx.features.call.WebRtcPeerConnectionManager
|
||||||
import im.vector.riotx.features.settings.VectorLocale.context
|
import im.vector.riotx.features.settings.VectorLocale.context
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
class CallHeadsUpActionReceiver : BroadcastReceiver() {
|
class CallHeadsUpActionReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
|
private lateinit var peerConnectionManager: WebRtcPeerConnectionManager
|
||||||
|
|
||||||
|
init {
|
||||||
|
val appContext = context.applicationContext
|
||||||
|
if (appContext is HasVectorInjector) {
|
||||||
|
peerConnectionManager = appContext.injector().webRtcPeerConnectionManager()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onReceive(context: Context, intent: Intent?) {
|
override fun onReceive(context: Context, intent: Intent?) {
|
||||||
when (intent?.getIntExtra(CallHeadsUpService.EXTRA_CALL_ACTION_KEY, 0)) {
|
when (intent?.getIntExtra(CallHeadsUpService.EXTRA_CALL_ACTION_KEY, 0)) {
|
||||||
CallHeadsUpService.CALL_ACTION_ANSWER -> onCallAnswerClicked()
|
CallHeadsUpService.CALL_ACTION_ANSWER -> onCallAnswerClicked()
|
||||||
|
@ -33,15 +44,10 @@ class CallHeadsUpActionReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
private fun onCallRejectClicked() {
|
private fun onCallRejectClicked() {
|
||||||
Timber.d("onCallRejectClicked")
|
Timber.d("onCallRejectClicked")
|
||||||
stopService()
|
peerConnectionManager.endCall()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onCallAnswerClicked() {
|
private fun onCallAnswerClicked() {
|
||||||
Timber.d("onCallAnswerClicked")
|
Timber.d("onCallAnswerClicked")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun stopService() {
|
|
||||||
context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
|
|
||||||
context.stopService(Intent(context, CallHeadsUpService::class.java))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue