From 374790176f8b0389fc728a0eb9901d8a0ce5757c Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 19 Jun 2020 10:11:45 +0200 Subject: [PATCH] Toggle HD/SD --- .../features/call/CallControlsBottomSheet.kt | 23 ++++++++++++++++ .../im/vector/riotx/features/call/Cameras.kt | 5 ++++ .../call/SharedActiveCallViewModel.kt | 12 +++------ .../features/call/VectorCallViewModel.kt | 16 ++++++++--- .../call/WebRtcPeerConnectionManager.kt | 27 +++++++++++++++---- vector/src/main/res/drawable/ic_hd.xml | 9 +++++++ .../src/main/res/drawable/ic_hd_disabled.xml | 9 +++++++ .../res/layout/bottom_sheet_call_controls.xml | 9 +++++++ vector/src/main/res/values/strings.xml | 3 +++ 9 files changed, 97 insertions(+), 16 deletions(-) create mode 100644 vector/src/main/res/drawable/ic_hd.xml create mode 100644 vector/src/main/res/drawable/ic_hd_disabled.xml diff --git a/vector/src/main/java/im/vector/riotx/features/call/CallControlsBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/call/CallControlsBottomSheet.kt index e502bcd7b1..e230592acf 100644 --- a/vector/src/main/java/im/vector/riotx/features/call/CallControlsBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/call/CallControlsBottomSheet.kt @@ -19,11 +19,14 @@ package im.vector.riotx.features.call import android.os.Bundle import android.view.View import androidx.appcompat.app.AlertDialog +import androidx.core.content.ContextCompat import androidx.core.view.isVisible import com.airbnb.mvrx.activityViewModel import im.vector.riotx.R import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment +import kotlinx.android.synthetic.main.activity_call.* import kotlinx.android.synthetic.main.bottom_sheet_call_controls.* +import kotlinx.android.synthetic.main.vector_preference_push_rule.view.* import me.gujun.android.span.span class CallControlsBottomSheet : VectorBaseBottomSheetDialogFragment() { @@ -47,6 +50,11 @@ class CallControlsBottomSheet : VectorBaseBottomSheetDialogFragment() { dismiss() } + callControlsToggleSDHD.clickableView.debouncedClicks { + callViewModel.handle(VectorCallViewActions.ToggleHDSD) + dismiss() + } + callViewModel.observeViewEvents { when (it) { is VectorCallViewEvents.ShowSoundDeviceChooser -> { @@ -112,5 +120,20 @@ class CallControlsBottomSheet : VectorBaseBottomSheetDialogFragment() { callControlsSwitchCamera.isVisible = state.canSwitchCamera callControlsSwitchCamera.subTitle = getString(if (state.isFrontCamera) R.string.call_camera_front else R.string.call_camera_back) + + if (state.isVideoCall) { + callControlsToggleSDHD.isVisible = true + if (state.isHD) { + callControlsToggleSDHD.title = getString(R.string.call_format_turn_hd_off) + callControlsToggleSDHD.subTitle = null + callControlsToggleSDHD.leftIcon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_hd) + } else { + callControlsToggleSDHD.title = getString(R.string.call_format_turn_hd_on) + callControlsToggleSDHD.subTitle = null + callControlsToggleSDHD.leftIcon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_hd_disabled) + } + } else { + callControlsToggleSDHD.isVisible = false + } } } diff --git a/vector/src/main/java/im/vector/riotx/features/call/Cameras.kt b/vector/src/main/java/im/vector/riotx/features/call/Cameras.kt index a87dab83c4..07d563ca9c 100644 --- a/vector/src/main/java/im/vector/riotx/features/call/Cameras.kt +++ b/vector/src/main/java/im/vector/riotx/features/call/Cameras.kt @@ -25,3 +25,8 @@ data class CameraProxy( val name: String, val type: CameraType ) + +sealed class CaptureFormat(val width: Int, val height: Int, val fps: Int) { + object HD : CaptureFormat(1280, 720, 30) + object SD : CaptureFormat(640, 480, 30) +} diff --git a/vector/src/main/java/im/vector/riotx/features/call/SharedActiveCallViewModel.kt b/vector/src/main/java/im/vector/riotx/features/call/SharedActiveCallViewModel.kt index 22becc82f5..4900d39a32 100644 --- a/vector/src/main/java/im/vector/riotx/features/call/SharedActiveCallViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/call/SharedActiveCallViewModel.kt @@ -33,12 +33,12 @@ class SharedActiveCallViewModel @Inject constructor( val activeCall: MutableLiveData = MutableLiveData() - val callStateListener = object: MxCall.StateListener { + val callStateListener = object : MxCall.StateListener { override fun onStateUpdate(call: MxCall) { - if (activeCall.value?.callId == call.callId) { - activeCall.postValue(call) - } + if (activeCall.value?.callId == call.callId) { + activeCall.postValue(call) + } } } @@ -48,10 +48,6 @@ class SharedActiveCallViewModel @Inject constructor( activeCall.postValue(call) call?.addListener(callStateListener) } - - override fun onCaptureStateChanged(captureInError: Boolean) { - // nop - } } init { diff --git a/vector/src/main/java/im/vector/riotx/features/call/VectorCallViewModel.kt b/vector/src/main/java/im/vector/riotx/features/call/VectorCallViewModel.kt index baa3ed41fc..5c832f8f40 100644 --- a/vector/src/main/java/im/vector/riotx/features/call/VectorCallViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/call/VectorCallViewModel.kt @@ -48,6 +48,7 @@ data class VectorCallViewState( val isAudioMuted: Boolean = false, val isVideoEnabled: Boolean = true, val isVideoCaptureInError: Boolean = false, + val isHD: Boolean = false, val isFrontCamera: Boolean = true, val canSwitchCamera: Boolean = true, val soundDevice: CallAudioManager.SoundDevice = CallAudioManager.SoundDevice.PHONE, @@ -66,6 +67,7 @@ sealed class VectorCallViewActions : VectorViewModelAction { object SwitchSoundDevice : VectorCallViewActions() object HeadSetButtonPressed : VectorCallViewActions() object ToggleCamera : VectorCallViewActions() + object ToggleHDSD : VectorCallViewActions() } sealed class VectorCallViewEvents : VectorViewEvents { @@ -129,9 +131,12 @@ class VectorCallViewModel @AssistedInject constructor( override fun onCurrentCallChange(call: MxCall?) { } - override fun onCaptureStateChanged(captureInError: Boolean) { + override fun onCaptureStateChanged(mgr: WebRtcPeerConnectionManager) { setState { - copy(isVideoCaptureInError = captureInError) + copy( + isVideoCaptureInError = mgr.capturerIsInError, + isHD = mgr.currentCaptureFormat() is CaptureFormat.HD + ) } } @@ -174,7 +179,8 @@ class VectorCallViewModel @AssistedInject constructor( soundDevice = webRtcPeerConnectionManager.audioManager.getCurrentSoundDevice(), availableSoundDevices = webRtcPeerConnectionManager.audioManager.getAvailableSoundDevices(), isFrontCamera = webRtcPeerConnectionManager.currentCameraType() == CameraType.FRONT, - canSwitchCamera = webRtcPeerConnectionManager.canSwitchCamera() + canSwitchCamera = webRtcPeerConnectionManager.canSwitchCamera(), + isHD = mxCall.isVideoCall && webRtcPeerConnectionManager.currentCaptureFormat() is CaptureFormat.HD ) } } ?: run { @@ -253,6 +259,10 @@ class VectorCallViewModel @AssistedInject constructor( VectorCallViewActions.ToggleCamera -> { webRtcPeerConnectionManager.switchCamera() } + VectorCallViewActions.ToggleHDSD -> { + if (!state.isVideoCall) return@withState + webRtcPeerConnectionManager.setCaptureFormat(if (state.isHD) CaptureFormat.SD else CaptureFormat.HD) + } }.exhaustive } diff --git a/vector/src/main/java/im/vector/riotx/features/call/WebRtcPeerConnectionManager.kt b/vector/src/main/java/im/vector/riotx/features/call/WebRtcPeerConnectionManager.kt index 232f679828..215e5928f0 100644 --- a/vector/src/main/java/im/vector/riotx/features/call/WebRtcPeerConnectionManager.kt +++ b/vector/src/main/java/im/vector/riotx/features/call/WebRtcPeerConnectionManager.kt @@ -76,7 +76,7 @@ class WebRtcPeerConnectionManager @Inject constructor( interface CurrentCallListener { fun onCurrentCallChange(call: MxCall?) - fun onCaptureStateChanged(captureInError: Boolean) + fun onCaptureStateChanged(mgr: WebRtcPeerConnectionManager) {} fun onAudioDevicesChange(mgr: WebRtcPeerConnectionManager) {} fun onCameraChange(mgr: WebRtcPeerConnectionManager) {} } @@ -165,11 +165,13 @@ class WebRtcPeerConnectionManager @Inject constructor( private val availableCamera = ArrayList() private var cameraInUse: CameraProxy? = null + private var currentCaptureMode: CaptureFormat = CaptureFormat.HD + var capturerIsInError = false set(value) { field = value currentCallsListeners.forEach { - tryThis { it.onCaptureStateChanged(value) } + tryThis { it.onCaptureStateChanged(this) } } } @@ -336,7 +338,7 @@ class WebRtcPeerConnectionManager @Inject constructor( // Fallback for old android, try to restart capture when attached if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && capturerIsInError && call.mxCall.isVideoCall) { // try to restart capture? - videoCapturer?.startCapture(1280, 720, 30) + videoCapturer?.startCapture(currentCaptureMode.width, currentCaptureMode.height, currentCaptureMode.fps) } // sink existing tracks (configuration change, e.g screen rotation) attachViewRenderersInternal() @@ -463,7 +465,7 @@ class WebRtcPeerConnectionManager @Inject constructor( videoCapturer.initialize(surfaceTextureHelper, context.applicationContext, videoSource!!.capturerObserver) // HD - videoCapturer.startCapture(1280, 720, 30) + videoCapturer.startCapture(currentCaptureMode.width, currentCaptureMode.height, currentCaptureMode.fps) this.videoCapturer = videoCapturer val localVideoTrack = peerConnectionFactory!!.createVideoTrack("ARDAMSv0", videoSource) @@ -706,6 +708,21 @@ class WebRtcPeerConnectionManager @Inject constructor( return cameraInUse?.type } + fun setCaptureFormat(format: CaptureFormat) { + Timber.v("## VOIP setCaptureFormat $format") + currentCall ?: return + executor.execute { + // videoCapturer?.stopCapture() + videoCapturer?.changeCaptureFormat(format.width, format.height, format.fps) + currentCaptureMode = format + currentCallsListeners.forEach { tryThis { it.onCaptureStateChanged(this) } } + } + } + + fun currentCaptureFormat(): CaptureFormat { + return currentCaptureMode + } + fun endCall() { // Update service state CallService.onNoActiveCall(context) @@ -971,7 +988,7 @@ class WebRtcPeerConnectionManager @Inject constructor( if (this.cameraId == cameraId && currentCall?.mxCall?.callId == callId) { // re-start the capture // TODO notify that video is enabled - videoCapturer?.startCapture(1280, 720, 30) + videoCapturer?.startCapture(currentCaptureMode.width, currentCaptureMode.height, currentCaptureMode.fps) (context.getSystemService(Context.CAMERA_SERVICE) as? CameraManager) ?.unregisterAvailabilityCallback(this) } diff --git a/vector/src/main/res/drawable/ic_hd.xml b/vector/src/main/res/drawable/ic_hd.xml new file mode 100644 index 0000000000..3335724529 --- /dev/null +++ b/vector/src/main/res/drawable/ic_hd.xml @@ -0,0 +1,9 @@ + + + diff --git a/vector/src/main/res/drawable/ic_hd_disabled.xml b/vector/src/main/res/drawable/ic_hd_disabled.xml new file mode 100644 index 0000000000..6396b7bc7e --- /dev/null +++ b/vector/src/main/res/drawable/ic_hd_disabled.xml @@ -0,0 +1,9 @@ + + + diff --git a/vector/src/main/res/layout/bottom_sheet_call_controls.xml b/vector/src/main/res/layout/bottom_sheet_call_controls.xml index 69d06ddb08..275d50f457 100644 --- a/vector/src/main/res/layout/bottom_sheet_call_controls.xml +++ b/vector/src/main/res/layout/bottom_sheet_call_controls.xml @@ -25,4 +25,13 @@ app:leftIcon="@drawable/ic_video_flip" app:tint="?attr/riotx_text_primary" /> + + diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index dbacf12caf..caabd55651 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -222,6 +222,9 @@ Switch Camera Front Back + Turn HD off + Turn HD on + Send files