feat: setting up rotation for the remote render view

This commit is contained in:
jubb 2022-02-15 16:17:14 +11:00
parent eb2e3d075e
commit a11a5da7c2
8 changed files with 86 additions and 20 deletions

View File

@ -44,6 +44,8 @@ import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_PRE_INIT
import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_RINGING import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_RINGING
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager.AudioDevice.EARPIECE import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager.AudioDevice.EARPIECE
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager.AudioDevice.SPEAKER_PHONE import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager.AudioDevice.SPEAKER_PHONE
import org.thoughtcrime.securesms.webrtc.data.quadrantRotation
import org.thoughtcrime.securesms.webrtc.video.RotationVideoProcessor
@AndroidEntryPoint @AndroidEntryPoint
class WebRtcCallActivity : PassphraseRequiredActionBarActivity() { class WebRtcCallActivity : PassphraseRequiredActionBarActivity() {
@ -73,7 +75,7 @@ class WebRtcCallActivity : PassphraseRequiredActionBarActivity() {
private val rotationListener by lazy { private val rotationListener by lazy {
object : OrientationEventListener(this) { object : OrientationEventListener(this) {
override fun onOrientationChanged(orientation: Int) { override fun onOrientationChanged(orientation: Int) {
viewModel.setDeviceRotation(orientation) viewModel.deviceRotation = orientation
} }
} }
} }

View File

@ -2,6 +2,8 @@ package org.thoughtcrime.securesms.webrtc
import android.content.Context import android.content.Context
import android.telephony.TelephonyManager import android.telephony.TelephonyManager
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.*
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -182,10 +184,12 @@ class CallManager(context: Context, audioManager: AudioManagerCompat, private va
eglBase = base eglBase = base
localRenderer = SurfaceViewRenderer(context).apply { localRenderer = SurfaceViewRenderer(context).apply {
setEnableHardwareScaler(true) setEnableHardwareScaler(true)
setScalingType(SCALE_ASPECT_FIT)
} }
remoteRenderer = SurfaceViewRenderer(context).apply { remoteRenderer = SurfaceViewRenderer(context).apply {
setEnableHardwareScaler(true) setEnableHardwareScaler(true)
setScalingType(SCALE_ASPECT_FIT)
} }
localRenderer?.init(base.eglBaseContext, null) localRenderer?.init(base.eglBaseContext, null)
@ -198,7 +202,6 @@ class CallManager(context: Context, audioManager: AudioManagerCompat, private va
Log.d("Loki", "remote rotation: $p2") Log.d("Loki", "remote rotation: $p2")
} }
}) })
remoteRenderer?.setScalingType(SCALE_ASPECT_FIT)
val encoderFactory = DefaultVideoEncoderFactory(base.eglBaseContext, true, true) val encoderFactory = DefaultVideoEncoderFactory(base.eglBaseContext, true, true)
val decoderFactory = DefaultVideoDecoderFactory(base.eglBaseContext) val decoderFactory = DefaultVideoDecoderFactory(base.eglBaseContext)

View File

@ -66,9 +66,11 @@ class CallViewModel @Inject constructor(private val callManager: CallManager): V
val remoteVideoEnabledState val remoteVideoEnabledState
get() = callManager.remoteVideoEvents.map { it.isEnabled } get() = callManager.remoteVideoEvents.map { it.isEnabled }
fun setDeviceRotation(newRotation: Int) { var deviceRotation: Int = 0
callManager.setDeviceRotation(newRotation) set(value) {
} field = value
callManager.setDeviceRotation(value)
}
val currentCallState val currentCallState
get() = callManager.currentCallState get() = callManager.currentCallState

View File

@ -1,10 +1,12 @@
package org.thoughtcrime.securesms.webrtc package org.thoughtcrime.securesms.webrtc
import android.content.Context import android.content.Context
import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.SettableFuture import org.session.libsignal.utilities.SettableFuture
import org.thoughtcrime.securesms.webrtc.video.Camera import org.thoughtcrime.securesms.webrtc.video.Camera
import org.thoughtcrime.securesms.webrtc.video.CameraEventListener import org.thoughtcrime.securesms.webrtc.video.CameraEventListener
import org.thoughtcrime.securesms.webrtc.video.CameraState import org.thoughtcrime.securesms.webrtc.video.CameraState
import org.thoughtcrime.securesms.webrtc.video.RotationVideoProcessor
import org.thoughtcrime.securesms.webrtc.video.RotationVideoSink import org.thoughtcrime.securesms.webrtc.video.RotationVideoSink
import org.webrtc.* import org.webrtc.*
import java.security.SecureRandom import java.security.SecureRandom
@ -74,7 +76,7 @@ class PeerConnectionWrapper(context: Context,
context, context,
rotationVideoSink rotationVideoSink
) )
videoTrack.addSink(localRenderer) rotationVideoSink.setSink(localRenderer)
videoTrack.setEnabled(false) videoTrack.setEnabled(false)
mediaStream.addTrack(videoTrack) mediaStream.addTrack(videoTrack)
} else { } else {
@ -278,6 +280,7 @@ class PeerConnectionWrapper(context: Context,
} }
fun setDeviceRotation(rotation: Int) { fun setDeviceRotation(rotation: Int) {
Log.d("Loki", "rotation: $rotation")
rotationVideoSink.rotation = rotation rotationVideoSink.rotation = rotation
} }

View File

@ -0,0 +1,11 @@
package org.thoughtcrime.securesms.webrtc.data
// get the video rotation from a specific rotation, locked into 90 degree
// chunks offset by 45 degrees
fun Int.quadrantRotation() = when (this % 360) {
in 315 until 360,
in 0 until 45 -> 90
in 45 until 135 -> 180
in 135 until 225 -> 270
else -> 0
}

View File

@ -0,0 +1,35 @@
package org.thoughtcrime.securesms.webrtc.video
import kotlinx.coroutines.newSingleThreadContext
import org.webrtc.VideoFrame
import org.webrtc.VideoProcessor
import org.webrtc.VideoSink
class RotationVideoProcessor: VideoProcessor {
private var isCapturing: Boolean = true
private var sink: VideoSink? = null
var rotation: Int = 0
override fun onCapturerStarted(p0: Boolean) {
isCapturing = true
}
override fun onCapturerStopped() {
isCapturing = false
}
override fun onFrameCaptured(frame: VideoFrame?) {
val thisSink = sink ?: return
val thisFrame = frame ?: return
val newFrame = VideoFrame(thisFrame.buffer, rotation, thisFrame.timestampNs)
thisSink.onFrame(newFrame)
}
override fun setSink(newSink: VideoSink?) {
sink = newSink
}
}

View File

@ -1,17 +1,20 @@
package org.thoughtcrime.securesms.webrtc.video package org.thoughtcrime.securesms.webrtc.video
import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.webrtc.data.quadrantRotation
import org.webrtc.CapturerObserver import org.webrtc.CapturerObserver
import org.webrtc.VideoFrame import org.webrtc.VideoFrame
import org.webrtc.VideoProcessor
import org.webrtc.VideoSink
import java.lang.ref.SoftReference import java.lang.ref.SoftReference
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
class RotationVideoSink: CapturerObserver { class RotationVideoSink: CapturerObserver, VideoProcessor {
var rotation: Int = 0 var rotation: Int = 0
private val capturing = AtomicBoolean(false) private val capturing = AtomicBoolean(false)
private var capturerObserver = SoftReference<CapturerObserver>(null) private var capturerObserver = SoftReference<CapturerObserver>(null)
private var sink = SoftReference<VideoSink>(null)
override fun onCapturerStarted(ignored: Boolean) { override fun onCapturerStarted(ignored: Boolean) {
capturing.set(true) capturing.set(true)
@ -26,15 +29,19 @@ class RotationVideoSink: CapturerObserver {
val observer = capturerObserver.get() val observer = capturerObserver.get()
if (videoFrame == null || observer == null || !capturing.get()) return if (videoFrame == null || observer == null || !capturing.get()) return
val quadrantRotation = when (rotation % 360) { val quadrantRotation = rotation.quadrantRotation()
in 0 until 90 -> 90
in 90 until 180 -> 180 val localRotation = 90
in 180 until 270 -> 270
else -> 0
}
val newFrame = VideoFrame(videoFrame.buffer, quadrantRotation, videoFrame.timestampNs) val newFrame = VideoFrame(videoFrame.buffer, quadrantRotation, videoFrame.timestampNs)
val localFrame = VideoFrame(videoFrame.buffer, localRotation, videoFrame.timestampNs)
observer.onFrameCaptured(newFrame) observer.onFrameCaptured(newFrame)
sink.get()?.onFrame(localFrame)
}
override fun setSink(sink: VideoSink?) {
this.sink = SoftReference(sink)
} }
fun setObserver(videoSink: CapturerObserver?) { fun setObserver(videoSink: CapturerObserver?) {

View File

@ -9,15 +9,18 @@
<FrameLayout <FrameLayout
android:id="@+id/remote_parent" android:id="@+id/remote_parent"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="match_parent"> android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent">
<FrameLayout <FrameLayout
android:id="@+id/remote_renderer" android:id="@+id/remote_renderer"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent" android:layout_gravity="center"/>
app:layout_constraintTop_toTopOf="parent" />
</FrameLayout> </FrameLayout>
<ImageView <ImageView
android:id="@+id/remote_recipient" android:id="@+id/remote_recipient"