diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/WebRtcCallActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/WebRtcCallActivity.kt index b722d3612..3836458c5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/WebRtcCallActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/WebRtcCallActivity.kt @@ -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.audio.SignalAudioManager.AudioDevice.EARPIECE 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 class WebRtcCallActivity : PassphraseRequiredActionBarActivity() { @@ -73,7 +75,7 @@ class WebRtcCallActivity : PassphraseRequiredActionBarActivity() { private val rotationListener by lazy { object : OrientationEventListener(this) { override fun onOrientationChanged(orientation: Int) { - viewModel.setDeviceRotation(orientation) + viewModel.deviceRotation = orientation } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallManager.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallManager.kt index 8fa50111c..454d630e5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallManager.kt @@ -2,6 +2,8 @@ package org.thoughtcrime.securesms.webrtc import android.content.Context import android.telephony.TelephonyManager +import android.view.ViewGroup +import android.view.ViewGroup.LayoutParams.* import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.serialization.Serializable @@ -182,10 +184,12 @@ class CallManager(context: Context, audioManager: AudioManagerCompat, private va eglBase = base localRenderer = SurfaceViewRenderer(context).apply { setEnableHardwareScaler(true) + setScalingType(SCALE_ASPECT_FIT) } remoteRenderer = SurfaceViewRenderer(context).apply { setEnableHardwareScaler(true) + setScalingType(SCALE_ASPECT_FIT) } localRenderer?.init(base.eglBaseContext, null) @@ -198,7 +202,6 @@ class CallManager(context: Context, audioManager: AudioManagerCompat, private va Log.d("Loki", "remote rotation: $p2") } }) - remoteRenderer?.setScalingType(SCALE_ASPECT_FIT) val encoderFactory = DefaultVideoEncoderFactory(base.eglBaseContext, true, true) val decoderFactory = DefaultVideoDecoderFactory(base.eglBaseContext) diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallViewModel.kt index 0df75cadd..59d85700a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallViewModel.kt @@ -66,9 +66,11 @@ class CallViewModel @Inject constructor(private val callManager: CallManager): V val remoteVideoEnabledState get() = callManager.remoteVideoEvents.map { it.isEnabled } - fun setDeviceRotation(newRotation: Int) { - callManager.setDeviceRotation(newRotation) - } + var deviceRotation: Int = 0 + set(value) { + field = value + callManager.setDeviceRotation(value) + } val currentCallState get() = callManager.currentCallState diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.kt index 5f54d6064..f237becc5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.kt @@ -1,10 +1,12 @@ package org.thoughtcrime.securesms.webrtc import android.content.Context +import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.SettableFuture import org.thoughtcrime.securesms.webrtc.video.Camera import org.thoughtcrime.securesms.webrtc.video.CameraEventListener import org.thoughtcrime.securesms.webrtc.video.CameraState +import org.thoughtcrime.securesms.webrtc.video.RotationVideoProcessor import org.thoughtcrime.securesms.webrtc.video.RotationVideoSink import org.webrtc.* import java.security.SecureRandom @@ -74,7 +76,7 @@ class PeerConnectionWrapper(context: Context, context, rotationVideoSink ) - videoTrack.addSink(localRenderer) + rotationVideoSink.setSink(localRenderer) videoTrack.setEnabled(false) mediaStream.addTrack(videoTrack) } else { @@ -278,6 +280,7 @@ class PeerConnectionWrapper(context: Context, } fun setDeviceRotation(rotation: Int) { + Log.d("Loki", "rotation: $rotation") rotationVideoSink.rotation = rotation } diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/data/CallUtils.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/data/CallUtils.kt new file mode 100644 index 000000000..ce5342957 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/data/CallUtils.kt @@ -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 +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/video/RotationVideoProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/video/RotationVideoProcessor.kt new file mode 100644 index 000000000..27fa078da --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/video/RotationVideoProcessor.kt @@ -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 + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/video/RotationVideoSink.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/video/RotationVideoSink.kt index e79c3dda6..c0bdf5a10 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/video/RotationVideoSink.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/video/RotationVideoSink.kt @@ -1,17 +1,20 @@ 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.VideoFrame +import org.webrtc.VideoProcessor +import org.webrtc.VideoSink import java.lang.ref.SoftReference import java.util.concurrent.atomic.AtomicBoolean -class RotationVideoSink: CapturerObserver { +class RotationVideoSink: CapturerObserver, VideoProcessor { var rotation: Int = 0 private val capturing = AtomicBoolean(false) private var capturerObserver = SoftReference(null) + private var sink = SoftReference(null) override fun onCapturerStarted(ignored: Boolean) { capturing.set(true) @@ -26,15 +29,19 @@ class RotationVideoSink: CapturerObserver { val observer = capturerObserver.get() if (videoFrame == null || observer == null || !capturing.get()) return - val quadrantRotation = when (rotation % 360) { - in 0 until 90 -> 90 - in 90 until 180 -> 180 - in 180 until 270 -> 270 - else -> 0 - } + val quadrantRotation = rotation.quadrantRotation() + + val localRotation = 90 val newFrame = VideoFrame(videoFrame.buffer, quadrantRotation, videoFrame.timestampNs) + val localFrame = VideoFrame(videoFrame.buffer, localRotation, videoFrame.timestampNs) + observer.onFrameCaptured(newFrame) + sink.get()?.onFrame(localFrame) + } + + override fun setSink(sink: VideoSink?) { + this.sink = SoftReference(sink) } fun setObserver(videoSink: CapturerObserver?) { diff --git a/app/src/main/res/layout/activity_webrtc.xml b/app/src/main/res/layout/activity_webrtc.xml index 529b5e731..669c6794c 100644 --- a/app/src/main/res/layout/activity_webrtc.xml +++ b/app/src/main/res/layout/activity_webrtc.xml @@ -9,15 +9,18 @@ + android:layout_width="wrap_content" + 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"> + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center"/>