PIP aspect ratio should match video aspect ratio. fixes #4077

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java         | 27 
src/main/java/eu/siacs/conversations/ui/util/Rationals.java             | 26 
src/main/java/eu/siacs/conversations/ui/widget/SurfaceViewRenderer.java | 48 
src/main/res/layout/activity_rtp_session.xml                            |  4 
4 files changed, 98 insertions(+), 7 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java 🔗

@@ -1,5 +1,8 @@
 package eu.siacs.conversations.ui;
 
+import static java.util.Arrays.asList;
+import static eu.siacs.conversations.utils.PermissionUtils.getFirstDenied;
+
 import android.Manifest;
 import android.annotation.SuppressLint;
 import android.app.PictureInPictureParams;
@@ -55,6 +58,7 @@ import eu.siacs.conversations.services.AppRTCAudioManager;
 import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.ui.util.AvatarWorkerTask;
 import eu.siacs.conversations.ui.util.MainThreadExecutor;
+import eu.siacs.conversations.ui.util.Rationals;
 import eu.siacs.conversations.utils.PermissionUtils;
 import eu.siacs.conversations.utils.TimeFrameUtils;
 import eu.siacs.conversations.xml.Namespace;
@@ -65,10 +69,7 @@ import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
 import eu.siacs.conversations.xmpp.jingle.Media;
 import eu.siacs.conversations.xmpp.jingle.RtpEndUserState;
 
-import static eu.siacs.conversations.utils.PermissionUtils.getFirstDenied;
-import static java.util.Arrays.asList;
-
-public class RtpSessionActivity extends XmppActivity implements XmppConnectionService.OnJingleRtpConnectionUpdate {
+public class RtpSessionActivity extends XmppActivity implements XmppConnectionService.OnJingleRtpConnectionUpdate, eu.siacs.conversations.ui.widget.SurfaceViewRenderer.OnAspectRatioChanged {
 
     public static final String EXTRA_WITH = "with";
     public static final String EXTRA_SESSION_ID = "session_id";
@@ -446,12 +447,14 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
     public void onStart() {
         super.onStart();
         mHandler.postDelayed(mTickExecutor, CALL_DURATION_UPDATE_INTERVAL);
+        this.binding.remoteVideo.setOnAspectRatioChanged(this);
     }
 
     @Override
     public void onStop() {
         mHandler.removeCallbacks(mTickExecutor);
         binding.remoteVideo.release();
+        binding.remoteVideo.setOnAspectRatioChanged(null);
         binding.localVideo.release();
         final WeakReference<JingleRtpConnection> weakReference = this.rtpConnectionReference;
         final JingleRtpConnection jingleRtpConnection = weakReference == null ? null : weakReference.get();
@@ -515,9 +518,12 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
     @RequiresApi(api = Build.VERSION_CODES.O)
     private void startPictureInPicture() {
         try {
+            final Rational rational = this.binding.remoteVideo.getAspectRatio();
+            final Rational clippedRational = Rationals.clip(rational);
+            Log.d(Config.LOGTAG, "suggested rational " + rational + ". clipped to " + clippedRational);
             enterPictureInPictureMode(
                     new PictureInPictureParams.Builder()
-                            .setAspectRatio(new Rational(10, 16))
+                            .setAspectRatio(clippedRational)
                             .build()
             );
         } catch (final IllegalStateException e) {
@@ -526,6 +532,17 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
         }
     }
 
+    @Override
+    public void onAspectRatioChanged(final Rational rational) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isPictureInPicture()) {
+            final Rational clippedRational = Rationals.clip(rational);
+            Log.d(Config.LOGTAG, "suggested rational after aspect ratio change " + rational + ". clipped to " + clippedRational);
+            setPictureInPictureParams(new PictureInPictureParams.Builder()
+                    .setAspectRatio(clippedRational)
+                    .build());
+        }
+    }
+
     private boolean deviceSupportsPictureInPicture() {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
             return getPackageManager().hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE);

src/main/java/eu/siacs/conversations/ui/util/Rationals.java 🔗

@@ -0,0 +1,26 @@
+package eu.siacs.conversations.ui.util;
+
+import android.util.Rational;
+
+public final class Rationals {
+
+    //between 2.39:1 and 1:2.39 (inclusive).
+    private static final Rational MIN = new Rational(100,239);
+    private static final Rational MAX = new Rational(239,100);
+
+    private Rationals() {
+
+    }
+
+
+    public static Rational clip(final Rational input) {
+        if (input.compareTo(MIN) < 0) {
+            return MIN;
+        }
+        if (input.compareTo(MAX) > 0) {
+            return MAX;
+        }
+        return input;
+    }
+
+}

src/main/java/eu/siacs/conversations/ui/widget/SurfaceViewRenderer.java 🔗

@@ -0,0 +1,48 @@
+package eu.siacs.conversations.ui.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Rational;
+
+import eu.siacs.conversations.Config;
+
+public class SurfaceViewRenderer extends org.webrtc.SurfaceViewRenderer {
+
+    private Rational aspectRatio = new Rational(1,1);
+
+    private OnAspectRatioChanged onAspectRatioChanged;
+
+    public SurfaceViewRenderer(Context context) {
+        super(context);
+    }
+
+    public SurfaceViewRenderer(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public void onFrameResolutionChanged(int videoWidth, int videoHeight, int rotation) {
+        super.onFrameResolutionChanged(videoWidth, videoHeight, rotation);
+        final int rotatedWidth = rotation != 0 && rotation != 180 ? videoHeight : videoWidth;
+        final int rotatedHeight = rotation != 0 && rotation != 180 ? videoWidth : videoHeight;
+        final Rational currentRational = this.aspectRatio;
+        this.aspectRatio = new Rational(rotatedWidth, rotatedHeight);
+        Log.d(Config.LOGTAG,"onFrameResolutionChanged("+rotatedWidth+","+rotatedHeight+","+aspectRatio+")");
+        if (currentRational.equals(this.aspectRatio) || onAspectRatioChanged == null) {
+            return;
+        }
+        onAspectRatioChanged.onAspectRatioChanged(this.aspectRatio);
+    }
+
+    public void setOnAspectRatioChanged(final OnAspectRatioChanged onAspectRatioChanged) {
+        this.onAspectRatioChanged = onAspectRatioChanged;
+    }
+
+    public Rational getAspectRatio() {
+        return this.aspectRatio;
+    }
+
+    public interface OnAspectRatioChanged {
+        void onAspectRatioChanged(final Rational rational);
+    }
+}

src/main/res/layout/activity_rtp_session.xml 🔗

@@ -98,13 +98,13 @@
             android:gravity="center"
             android:visibility="gone">
 
-            <org.webrtc.SurfaceViewRenderer
+            <eu.siacs.conversations.ui.widget.SurfaceViewRenderer
                 android:id="@+id/remote_video"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content" />
         </LinearLayout>
 
-        <org.webrtc.SurfaceViewRenderer
+        <eu.siacs.conversations.ui.widget.SurfaceViewRenderer
             android:id="@+id/local_video"
             android:layout_width="@dimen/local_video_preview_width"
             android:layout_height="@dimen/local_video_preview_height"