androidreact-nativekotlinexoplayer

Exoplayer: video does not resize to fit when initial orientation is portrait


I am running into an issue where the Exoplayer playerview isn't initially fitting the containing frame, and maintaining the video's aspect ratio in portrait. The view is only resized correctly when rotating the device to landscape, and then rotating back to portrait orientation.

Note, this is a React Native application and the orientation for the rest of the app is locked to portrait. In this case, only the player is to freely move between different orientations.

What I have tried so far:

I am at a little bit of a loss right now. My understand is, setting the resize_mode to fit as the default should work when the player view is created. Is there something with how the layout is created? Any help is appreciated.

For reference, here are parts of the code for the player:

video.xml - layout

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="@color/black">
    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/black">
        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/content_container"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent">
            <com.google.android.exoplayer2.ui.PlayerView
                android:id="@+id/exoplayerview_video"
                app:resize_mode="fit"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
        </androidx.constraintlayout.widget.ConstraintLayout>
    </androidx.constraintlayout.widget.ConstraintLayout>
</merge>

Main Activity - android manifest (partial)

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <application
        android:name=".MainApplication"
        android:allowBackup="false"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:networkSecurityConfig="@xml/network_security_config"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:theme="@style/AppTheme"
        android:extractNativeLibs="${extractNativeLibs}"
        android:supportsRtl="true">
        <meta-data
            android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
            android:value="com.reactnative.googlecast.GoogleCastOptionsProvider" />
        <meta-data
            android:name="com.reactnative.googlecast.RECEIVER_APPLICATION_ID"
            android:value="${castReceiverId}" />
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="${google_maps_api_key}" />
        <activity
            android:name=".MainActivity"
            android:theme="@style/SplashTheme"
            android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
            android:screenOrientation="portrait"
            android:exported="true"
            android:label="@string/app_name"
            android:windowSoftInputMode="adjustResize"
            android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

 ............

Here are some screenshots of what I am seeing vs. what is intended:

Initial Portrait issue - resize mode not respected: enter image description here

Expected player view sizing: enter image description here


Additional notes:


Solution

  • In case any one else comes across this problem, I found the issue. There appears to be an open (as of 5/21/24) issue on react native's github: https://github.com/facebook/react-native/issues/17968. In short, you may run into re-layout problems when trying to update custom native views and their children.

    My solution was to request a re-layout of my player's view.

    My Main player class:

    class VideoPlayer(context: ThemedReactContext, type: PlayerType) : MediaPlayer(
            context,
            R.layout.video) {
    
        // Called by `requestLayout`
        private val measureAndLayout = Runnable {
            measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))
    
            layout(left, top, right, bottom)
        }
    
        override fun requestLayout() {
            super.requestLayout()
            // refresh view & children layouts by triggering a measure & layout
            // pass after a requestLayout() call.
            // ===================================================================
            // active issue: https://github.com/facebook/react-native/issues/17968
            post(measureAndLayout)
        }
    
        // ........
    
    }
    

    Inside my player's controller:

    // called on my player's main view -- I invoked this on init of my controller, which was enough
    view.requestLayout()
    

    disclaimer: I'm still on react-native version 0.71.1, so not sure if this is still an issue on newer versions using the new RN architecture