feat: setting up rotation for the remote render view
This commit is contained in:
parent
eb2e3d075e
commit
a11a5da7c2
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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?) {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in New Issue