mirror of
https://github.com/oxen-io/session-android.git
synced 2025-08-24 22:17:25 +00:00
feat: add call stats report on frontend
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
package org.thoughtcrime.securesms.calls
|
||||
|
||||
import android.Manifest
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Bundle
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.android.synthetic.main.activity_webrtc_tests.*
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -18,6 +20,7 @@ import org.session.libsession.utilities.Debouncer
|
||||
import org.session.libsignal.protos.SignalServiceProtos
|
||||
import org.session.libsignal.utilities.Log
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||
import org.thoughtcrime.securesms.permissions.Permissions
|
||||
import org.webrtc.*
|
||||
|
||||
|
||||
@@ -63,6 +66,26 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection
|
||||
private val candidates: MutableList<IceCandidate> = mutableListOf()
|
||||
private val iceDebouncer = Debouncer(2_000)
|
||||
|
||||
private var localCandidateType: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
if (value != null) {
|
||||
// show it
|
||||
local_candidate_info.isVisible = true
|
||||
local_candidate_info.text = "local: $value"
|
||||
}
|
||||
}
|
||||
|
||||
private var remoteCandidateType: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
if (value != null) {
|
||||
remote_candidate_info.isVisible = true
|
||||
remote_candidate_info.text = "remote: $value"
|
||||
}
|
||||
// update text
|
||||
}
|
||||
|
||||
private lateinit var callAddress: Address
|
||||
private val peerConnection by lazy {
|
||||
// TODO: in a lokinet world, ice servers shouldn't be needed as .loki addresses should suffice to p2p
|
||||
@@ -80,14 +103,20 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection
|
||||
super.onCreate(savedInstanceState, ready)
|
||||
setContentView(R.layout.activity_webrtc_tests)
|
||||
|
||||
//TODO: better handling of permissions
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)
|
||||
.onAllGranted {
|
||||
setupStreams()
|
||||
}
|
||||
.execute()
|
||||
|
||||
local_renderer.run {
|
||||
setMirror(true)
|
||||
setEnableHardwareScaler(true)
|
||||
init(eglBase.eglBaseContext, null)
|
||||
}
|
||||
|
||||
remote_renderer.run {
|
||||
setMirror(true)
|
||||
setEnableHardwareScaler(true)
|
||||
init(eglBase.eglBaseContext, null)
|
||||
}
|
||||
@@ -104,24 +133,6 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection
|
||||
|
||||
}
|
||||
|
||||
val videoSource = connectionFactory.createVideoSource(false)
|
||||
|
||||
videoCapturer?.initialize(surfaceHelper, local_renderer.context, videoSource.capturerObserver) ?: run {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
videoCapturer?.startCapture(HD_VIDEO_WIDTH, HD_VIDEO_HEIGHT, 10)
|
||||
|
||||
val audioTrack = connectionFactory.createAudioTrack(LOCAL_TRACK_ID + "_audio", audioSource)
|
||||
val videoTrack = connectionFactory.createVideoTrack(LOCAL_TRACK_ID, videoSource)
|
||||
videoTrack.addSink(local_renderer)
|
||||
|
||||
val stream = connectionFactory.createLocalMediaStream(LOCAL_STREAM_ID)
|
||||
stream.addTrack(videoTrack)
|
||||
stream.addTrack(audioTrack)
|
||||
|
||||
peerConnection.addStream(stream)
|
||||
|
||||
// create either call or answer
|
||||
if (intent.action == ACTION_ANSWER) {
|
||||
callAddress = intent.getParcelableExtra(EXTRA_ADDRESS) ?: run { finish(); return }
|
||||
@@ -175,11 +186,27 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection
|
||||
|
||||
override fun onStatsDelivered(statsReport: RTCStatsReport?) {
|
||||
statsReport?.let { report ->
|
||||
val usedConnection = report.statsMap.filter { (_,v) -> v.type == "candidate-pair" && v.members["writable"] == true }.asIterable().firstOrNull()?.value ?: return@let
|
||||
|
||||
usedConnection.members["remoteCandidateId"]?.let { candidate ->
|
||||
runOnUiThread {
|
||||
remoteCandidateType = report.statsMap[candidate]?.members?.get("candidateType") as? String
|
||||
}
|
||||
}
|
||||
|
||||
usedConnection.members["localCandidateId"]?.let { candidate ->
|
||||
runOnUiThread {
|
||||
localCandidateType = report.statsMap[candidate]?.members?.get("candidateType") as? String
|
||||
}
|
||||
}
|
||||
|
||||
Log.d("Loki-RTC", "report is: $report")
|
||||
}
|
||||
}
|
||||
|
||||
private fun endCall() {
|
||||
if (isFinishing) return
|
||||
|
||||
MessageSender.sendNonDurably(
|
||||
CallMessage.endCall(),
|
||||
callAddress
|
||||
@@ -193,6 +220,26 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection
|
||||
endCall()
|
||||
}
|
||||
|
||||
private fun setupStreams() {
|
||||
val videoSource = connectionFactory.createVideoSource(false)
|
||||
|
||||
videoCapturer?.initialize(surfaceHelper, local_renderer.context, videoSource.capturerObserver) ?: run {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
videoCapturer?.startCapture(HD_VIDEO_WIDTH, HD_VIDEO_HEIGHT, 10)
|
||||
|
||||
val audioTrack = connectionFactory.createAudioTrack(LOCAL_TRACK_ID + "_audio", audioSource)
|
||||
val videoTrack = connectionFactory.createVideoTrack(LOCAL_TRACK_ID, videoSource)
|
||||
videoTrack.addSink(local_renderer)
|
||||
|
||||
val stream = connectionFactory.createLocalMediaStream(LOCAL_STREAM_ID)
|
||||
stream.addTrack(videoTrack)
|
||||
stream.addTrack(audioTrack)
|
||||
|
||||
peerConnection.addStream(stream)
|
||||
}
|
||||
|
||||
private fun createCameraCapturer(enumerator: CameraEnumerator): CameraVideoCapturer? {
|
||||
val deviceNames = enumerator.deviceNames
|
||||
|
||||
@@ -237,16 +284,6 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection
|
||||
|
||||
override fun onIceGatheringChange(p0: PeerConnection.IceGatheringState?) {
|
||||
Log.d("Loki-RTC", "onIceGatheringChange: $p0")
|
||||
p0 ?: return
|
||||
Log.d("Loki-RTC","sending IceCandidates of size: ${candidates.size}")
|
||||
MessageSender.sendNonDurably(
|
||||
CallMessage(SignalServiceProtos.CallMessage.Type.ICE_CANDIDATES,
|
||||
candidates.map { it.sdp },
|
||||
candidates.map { it.sdpMLineIndex },
|
||||
candidates.map { it.sdpMid }
|
||||
),
|
||||
callAddress
|
||||
)
|
||||
}
|
||||
|
||||
override fun onIceCandidate(iceCandidate: IceCandidate?) {
|
||||
|
@@ -14,6 +14,7 @@ import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.loader.app.LoaderManager
|
||||
@@ -150,13 +151,15 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), ConversationClickLis
|
||||
when (message.type) {
|
||||
OFFER -> {
|
||||
// show bottom sheet
|
||||
CallBottomSheet().apply {
|
||||
arguments = bundleOf(
|
||||
CallBottomSheet.ARGUMENT_ADDRESS to sender,
|
||||
CallBottomSheet.ARGUMENT_SDP to message.sdps.toTypedArray(),
|
||||
CallBottomSheet.ARGUMENT_TYPE to message.type!!.number
|
||||
)
|
||||
show(this@HomeActivity.supportFragmentManager,"call-sheet")
|
||||
if (lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
|
||||
CallBottomSheet().apply {
|
||||
arguments = bundleOf(
|
||||
CallBottomSheet.ARGUMENT_ADDRESS to sender,
|
||||
CallBottomSheet.ARGUMENT_SDP to message.sdps.toTypedArray(),
|
||||
CallBottomSheet.ARGUMENT_TYPE to message.type!!.number
|
||||
)
|
||||
show(this@HomeActivity.supportFragmentManager,"call-sheet")
|
||||
}
|
||||
}
|
||||
}
|
||||
END_CALL -> {
|
||||
|
@@ -2,7 +2,42 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<TextView
|
||||
android:padding="@dimen/small_spacing"
|
||||
android:elevation="8dp"
|
||||
tools:visibility="visible"
|
||||
android:textColor="@color/white"
|
||||
tools:text="relay"
|
||||
android:visibility="gone"
|
||||
android:background="@drawable/pill"
|
||||
android:backgroundTint="@color/black"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:id="@+id/local_candidate_info"
|
||||
app:layout_constraintEnd_toStartOf="@id/remote_candidate_info"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:padding="@dimen/small_spacing"
|
||||
android:elevation="8dp"
|
||||
tools:visibility="visible"
|
||||
tools:text="relay"
|
||||
android:visibility="gone"
|
||||
android:textColor="@color/white"
|
||||
android:background="@drawable/pill"
|
||||
android:backgroundTint="@color/black"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:id="@+id/remote_candidate_info"
|
||||
app:layout_constraintStart_toEndOf="@id/local_candidate_info"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
|
||||
<org.webrtc.SurfaceViewRenderer
|
||||
|
Reference in New Issue
Block a user