Try to start streaming screen capture.

This commit is contained in:
Onuray Sahin 2022-04-22 15:50:40 +03:00
parent 8eaa2f8dfb
commit 7939ecaedc
5 changed files with 62 additions and 12 deletions

View File

@ -24,6 +24,7 @@ import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP import android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Color import android.graphics.Color
import android.media.projection.MediaProjection
import android.media.projection.MediaProjectionManager import android.media.projection.MediaProjectionManager
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
@ -32,6 +33,7 @@ import android.util.Rational
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import androidx.activity.result.ActivityResult
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
@ -76,6 +78,7 @@ import org.matrix.android.sdk.api.session.call.TurnServerResponse
import org.matrix.android.sdk.api.session.room.model.call.EndCallReason import org.matrix.android.sdk.api.session.room.model.call.EndCallReason
import org.webrtc.EglBase import org.webrtc.EglBase
import org.webrtc.RendererCommon import org.webrtc.RendererCommon
import org.webrtc.ScreenCapturerAndroid
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -636,18 +639,36 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
private val screenSharingPermissionActivityResultLauncher = registerStartForActivityResult { activityResult -> private val screenSharingPermissionActivityResultLauncher = registerStartForActivityResult { activityResult ->
if (activityResult.resultCode == Activity.RESULT_OK) { if (activityResult.resultCode == Activity.RESULT_OK) {
callViewModel.handle(VectorCallViewActions.StartScreenSharing)
// We need to start a foreground service with a sticky notification during screen sharing
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContextCompat.startForegroundService( // We need to start a foreground service with a sticky notification during screen sharing
this, startScreenSharingService(activityResult)
Intent(this, ScreenCaptureService::class.java) } else {
) startScreenSharing(activityResult)
screenCaptureServiceConnection.bind()
} }
} }
} }
private fun startScreenSharing(activityResult: ActivityResult) {
val videoCapturer = ScreenCapturerAndroid(activityResult.data, object : MediaProjection.Callback() {
override fun onStop() {
Timber.v("User revoked the screen capturing permission")
}
})
callViewModel.handle(VectorCallViewActions.StartScreenSharing(videoCapturer))
}
private fun startScreenSharingService(activityResult: ActivityResult) {
ContextCompat.startForegroundService(
this,
Intent(this, ScreenCaptureService::class.java)
)
screenCaptureServiceConnection.bind(object : ScreenCaptureServiceConnection.Callback {
override fun onServiceConnected() {
startScreenSharingService(activityResult)
}
})
}
private fun handleShowScreenSharingPermissionDialog() { private fun handleShowScreenSharingPermissionDialog() {
getSystemService<MediaProjectionManager>()?.let { getSystemService<MediaProjectionManager>()?.let {
navigator.openScreenSharingPermissionDialog(it.createScreenCaptureIntent(), screenSharingPermissionActivityResultLauncher) navigator.openScreenSharingPermissionDialog(it.createScreenCaptureIntent(), screenSharingPermissionActivityResultLauncher)

View File

@ -19,6 +19,7 @@ package im.vector.app.features.call
import im.vector.app.core.platform.VectorViewModelAction import im.vector.app.core.platform.VectorViewModelAction
import im.vector.app.features.call.audio.CallAudioManager import im.vector.app.features.call.audio.CallAudioManager
import im.vector.app.features.call.transfer.CallTransferResult import im.vector.app.features.call.transfer.CallTransferResult
import org.webrtc.VideoCapturer
sealed class VectorCallViewActions : VectorViewModelAction { sealed class VectorCallViewActions : VectorViewModelAction {
object EndCall : VectorCallViewActions() object EndCall : VectorCallViewActions()
@ -41,5 +42,5 @@ sealed class VectorCallViewActions : VectorViewModelAction {
data class CallTransferSelectionResult(val callTransferResult: CallTransferResult) : VectorCallViewActions() data class CallTransferSelectionResult(val callTransferResult: CallTransferResult) : VectorCallViewActions()
object TransferCall : VectorCallViewActions() object TransferCall : VectorCallViewActions()
object ToggleScreenSharing : VectorCallViewActions() object ToggleScreenSharing : VectorCallViewActions()
object StartScreenSharing : VectorCallViewActions() data class StartScreenSharing(val videoCapturer: VideoCapturer) : VectorCallViewActions()
} }

View File

@ -348,7 +348,7 @@ class VectorCallViewModel @AssistedInject constructor(
handleToggleScreenSharing(state.isSharingScreen) handleToggleScreenSharing(state.isSharingScreen)
} }
is VectorCallViewActions.StartScreenSharing -> { is VectorCallViewActions.StartScreenSharing -> {
call?.startSharingScreen() call?.startSharingScreen(action.videoCapturer)
setState { setState {
copy(isSharingScreen = true) copy(isSharingScreen = true)
} }

View File

@ -27,10 +27,17 @@ class ScreenCaptureServiceConnection @Inject constructor(
private val context: Context private val context: Context
) : ServiceConnection { ) : ServiceConnection {
interface Callback {
fun onServiceConnected()
}
private var isBound = false private var isBound = false
private var screenCaptureService: ScreenCaptureService? = null private var screenCaptureService: ScreenCaptureService? = null
private var callback: Callback? = null
fun bind(callback: Callback) {
this.callback = callback
fun bind() {
if (!isBound) { if (!isBound) {
Intent(context, ScreenCaptureService::class.java).also { intent -> Intent(context, ScreenCaptureService::class.java).also { intent ->
context.bindService(intent, this, 0) context.bindService(intent, this, 0)
@ -45,6 +52,7 @@ class ScreenCaptureServiceConnection @Inject constructor(
override fun onServiceConnected(className: ComponentName, binder: IBinder) { override fun onServiceConnected(className: ComponentName, binder: IBinder) {
screenCaptureService = (binder as ScreenCaptureService.LocalBinder).getService() screenCaptureService = (binder as ScreenCaptureService.LocalBinder).getService()
isBound = true isBound = true
callback?.onServiceConnected()
} }
override fun onServiceDisconnected(className: ComponentName) { override fun onServiceDisconnected(className: ComponentName) {

View File

@ -84,6 +84,7 @@ import org.webrtc.RtpTransceiver
import org.webrtc.SessionDescription import org.webrtc.SessionDescription
import org.webrtc.SurfaceTextureHelper import org.webrtc.SurfaceTextureHelper
import org.webrtc.SurfaceViewRenderer import org.webrtc.SurfaceViewRenderer
import org.webrtc.VideoCapturer
import org.webrtc.VideoSource import org.webrtc.VideoSource
import org.webrtc.VideoTrack import org.webrtc.VideoTrack
import timber.log.Timber import timber.log.Timber
@ -770,8 +771,27 @@ class WebRtcCall(
return currentCaptureFormat return currentCaptureFormat
} }
fun startSharingScreen() { fun startSharingScreen(videoCapturer: VideoCapturer) {
// TODO. Will be handled within the next PR. val factory = peerConnectionFactoryProvider.get() ?: return
val videoSource = factory.createVideoSource(true)
val audioSource = factory.createAudioSource(DEFAULT_AUDIO_CONSTRAINTS)
val surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", rootEglBase!!.eglBaseContext)
videoCapturer.initialize(surfaceTextureHelper, context, videoSource.capturerObserver)
videoCapturer.startCapture(currentCaptureFormat.width, currentCaptureFormat.height, currentCaptureFormat.fps)
val videoTrack = factory.createVideoTrack("ARDAMSv0", videoSource).apply { setEnabled(true) }
val audioTrack = factory.createAudioTrack("ARDAMSa0", audioSource).apply { setEnabled(true) }
val localMediaStream = factory.createLocalMediaStream("ARDAMS")
peerConnection?.addTrack(videoTrack)
peerConnection?.addTrack(audioTrack)
localMediaStream.addTrack(videoTrack)
localMediaStream.addTrack(audioTrack)
localAudioSource = audioSource
localVideoSource = videoSource
localAudioTrack = audioTrack
localVideoTrack = videoTrack
} }
fun stopSharingScreen() { fun stopSharingScreen() {