Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fullscreen the Exoplayer

I try to show the show video (.mp4) with exoplayer in RecyclerView and ViewPager. I show the video controller with custom layout. so far so good.

Now try to fullscreen the video like other video player how use before but can't find a good way in the exoplayer doc.

can anyone help me?

like image 238
Ashkan Avatar asked Jan 24 '18 11:01

Ashkan


4 Answers

The ExoPlayer library currently does not provide a built-in way to enable/disable fullscreen mode. You need to implement this yourself or find some third party code for this I'm afraid.

Essentially two steps are required:

a) Set the window and activity properties to fullscreen and/or immersive mode and (if desired) change to landscape mode. That's not difficult. See this page on Android Developers.

b) Transition rendering to a SimpleExoPlayerView (actually it's about the Surface) which covers the entire viewport in immersive mode. This is more of a challenge to achieve optimal user experience on all API levels.

For an optimal user experience, we want to keep the player playing while doing the transition to full screen and back, to continue playback seamlessly. In a RecyclerView this is kind of tricky to solve for all API levels.

Approach 1

The easiest way is probably having a separate instance of SimpleExoPlayerView which you put on top of your layout as soon as you entered immersive mode (some people open a dialog with the second view for this, some have the second view simply on top in the layout somehow and show/hide it on demand).

Then you detach the player instance from the SimpleExoPlayerView embedded in the RV and attach it to the fullscreen view with a call to a static helper method:

SimpleExoPlayerView.switchTargetView(simpleExoPlayer, oldPlayerView, newPlayerView);

This approach works very well on API >=23. With API 23 the method MediaCodec.setOutputSurface allowing to swap surfaces dynamically has been added. The static method above makes sure this technique is applied. As a result audio and video keep playing and the user experience from entering and exiting fullscreen is super smooth. For API <=22 a new codec instance needs to be create to swap to another surface. This interrupts playback and the user experience for this approach is degraded.

Approach 2

To avoid swapping to another surface on lower API level, you need to use a single surface and transition it to fullscreen somehow. You can either just hide everything else than the SimpleExoPlayerView and set the layout width and height to match it's parent or, you can replace the video view with a placeholder and put it on top and back.

This can work quite well for simple layouts but with complex layouts probably including fragments, viewpagers, recyclerviews this can be a quite intrusive operation causing something to flicker or interrupting playback shortly (on some API levels for instance when removing the player view from the view hierarchy). I've seen this working nicely for various layouts though.

Further approaches/challenges

There might be other and probably better approaches when you dig deeper and/or if you are not using SimpleExoPlayerView at all.

like image 96
marcbaechinger Avatar answered Oct 19 '22 17:10

marcbaechinger


You can easily do this from the XML of the ExoPlayer. Set the following attribute:

app:resize_mode="fill"
like image 21
BlackHatSamurai Avatar answered Oct 19 '22 17:10

BlackHatSamurai


To show the complete full screen video on expo player use this line in you xml file app:resize_mode="fill"

like image 3
eagerprince Avatar answered Oct 19 '22 17:10

eagerprince


Exoplayer doesn't provide the fullscreen so here is the workaround that worked for me. Here I have restricted the Screen to rotate and manually changing the orientation programmatically change the width and height of the player_view and has set the visibility of the toolbar gone. Used Data Binding and Kotlin.

Follows a code block for XML

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/video_player_container"
        android:layout_width="match_parent"
        android:layout_height="250dp"
        app:layout_constraintTop_toTopOf="parent">


        <com.google.android.exoplayer2.ui.SimpleExoPlayerView
            android:id="@+id/player_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:focusable="true"
            android:keepScreenOn="true"
            android:padding="0dp"
            app:controller_layout_id="@layout/exo_playback_control_view"
            app:layout_constraintTop_toTopOf="parent"
            app:resize_mode="fill"
            app:use_controller="true" />

        <LinearLayout
            android:id="@+id/nextVideoContainer"
            android:layout_width="wrap_content"
            android:layout_height="@dimen/spacing_32"
            android:background="#90000000"
            android:onClick="@{() -> vm.onNextVideo()}"
            android:orientation="horizontal"
            android:paddingLeft="@dimen/spacing_16"
            android:paddingRight="@dimen/spacing_16"
            android:visibility="@{vm.shouldShowNextBtn}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <com.sharedcode.widgets.CustomTextView
                android:id="@+id/next_video_label"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:text="@string/label_next"
                android:textColor="@android:color/white" />

            <com.sharedcode.widgets.CustomImageView
                android:id="@+id/next_video_image"
                android:layout_width="10dp"
                android:layout_height="10dp"
                android:layout_gravity="center_vertical"
                android:layout_marginStart="8dp"
                android:layout_marginLeft="8dp"
                android:paddingTop="2dp"
                android:src="@drawable/ic_play_next" />
        </LinearLayout>

        <RelativeLayout
            android:id="@+id/retry_container"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:background="#90000000"
            android:visibility="gone"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:onClick="@{() -> vm.onRetry()}">

            <com.sharedcode.widgets.CustomTextView
                android:id="@+id/txt_no_internet"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="0dp"
                android:layout_centerInParent="true"
                android:text="@string/txt_no_internet_connection"
                android:textColor="@android:color/white"
                android:textSize="@dimen/font_16" />

            <com.sharedcode.widgets.CustomTextView
                android:layout_width="`wrap_content`"
                android:layout_height="wrap_content"
                android:layout_below="@+id/txt_no_internet"
                android:layout_centerInParent="true"
                android:layout_marginTop="@dimen/spacing_16"
                android:maxHeight="@dimen/spacing_32"
                android:text="@string/txt_tap_to_retry"
                android:textColor="@android:color/white"
                android:textSize="@dimen/font_16" />


        </RelativeLayout>
     </androidx.constraintlayout.widget.ConstraintLayout>
    </androidx.constraintlayout.widget.ConstraintLayout>

Changes in Android Manifest

<activity android:name=".yourPackage.ClassName"
        android:screenOrientation="portrait"
        android:configChanges="orientation|screenSize|layoutDirection"/>

Check the orientation and rotate it via the following code

mBinding.playerView.exo_fullscreen_btn.setOnClickListener {
        if ((activity as TrainingVideoActivity).checkLandscapeOrientation()) {
            (activity as TrainingVideoActivity).changeOrientationToLandscape(false)

        } else {
            (activity as TrainingVideoActivity).changeOrientationToLandscape(true)

        }

    }

Methods Signature are following

/**
 * Changes the Orientation
 * @param shouldLandscape
 */
fun changeOrientationToLandscape(shouldLandscape: Boolean) {
    requestedOrientation = if (shouldLandscape) {
        ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
    } else {
        ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
    }
}

/**
 * Checks the Orientation
 * And returns true if Landscape else false
 */
fun checkLandscapeOrientation() : Boolean {
    val orientation = resources.configuration.orientation
    return orientation == Configuration.ORIENTATION_LANDSCAPE
}

Now Just Override onConfigurationChanged method in your fragment/activity, since here I have used Fragment. So here I have changed the width/height of the parent container in which ExoplayerView is placed.

/**
 * Used for Showing Video on Full Screen
 * @param newConfig
 * Used EXO_PLAYER_VIEW_HEIGHT as 250
 */
override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        hideToolbarAndShowFullScreen()
        mBinding.playerView.exo_fullscreen_btn.setImageDrawable(ContextCompat.getDrawable(activity!!, R.drawable.ic_shrink))
        val params = mBinding.videoPlayerContainer.layoutParams as ConstraintLayout.LayoutParams
        params.width = ViewGroup.LayoutParams.MATCH_PARENT
        params.height = ViewGroup.LayoutParams.MATCH_PARENT
        mBinding.videoPlayerContainer.layoutParams = params
    } else {
        showToolbarAndClearFullScreen()
        mBinding.playerView.exo_fullscreen_btn.setImageDrawable(ContextCompat.getDrawable(activity!!, R.drawable.ic_fullscreen))
        val params = mBinding.videoPlayerContainer.layoutParams as ConstraintLayout.LayoutParams
        val factor = mBinding.playerView.context.resources.displayMetrics.density
        params.width = ViewGroup.LayoutParams.MATCH_PARENT
        params.height = (EXO_PLAYER_VIEW_HEIGHT * factor).toInt()
        mBinding.videoPlayerContainer.layoutParams = params
    }
}

/**
 * Show the Toolbar and reset to original in the Portrait Mode
 */
private fun showToolbarAndClearFullScreen() {
    (activity as TrainingVideoActivity).supportActionBar!!.show()
    (activity as TrainingVideoActivity).window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)

}

Lastly, XML for the player_controller

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<RelativeLayout xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#99000000">


    <LinearLayout
        android:id="@+id/container_play_pause"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:orientation="horizontal">

        <ImageButton
            android:id="@id/exo_play"
            style="@style/ExoMediaButton.Play"
            android:src="@drawable/ic_play_exoplayer"
            />

        <ImageButton
            android:id="@id/exo_pause"
            style="@style/ExoMediaButton.Pause"
            android:src="@drawable/ic_pause_exoplayer"/>
    </LinearLayout>

    <LinearLayout
        android:id="@+id/seekbar_bottom"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_gravity="bottom"
        android:background="#CC000000"
        android:clickable="false"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="4dp"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            tools:ignore="UselessParent">

            <com.sharedcode.widgets.CustomTextView
                android:id="@id/exo_position"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:includeFontPadding="false"
                android:paddingLeft="4dp"
                android:paddingRight="4dp"
                android:textColor="#FFBEBEBE"
                android:textSize="14sp"
                android:textStyle="bold" />

            <com.bnb.paynearby.utils.exoplayer.ExtendedTimebar
                android:id="@id/exo_progress"
                android:layout_width="0dp"
                android:layout_height="50dp"
                android:layout_weight="1"
                app:buffered_color="@color/white"
                app:played_color="@color/color_red"
                app:scrubber_color="@color/color_red"
                app:scrubber_disabled_size="10dp"
                app:unplayed_color="#484848" />

            <com.sharedcode.widgets.CustomTextView
                android:id="@id/exo_duration"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:clickable="false"
                android:includeFontPadding="false"
                android:paddingLeft="4dp"
                android:paddingRight="4dp"
                android:textColor="#FFBEBEBE"
                android:textSize="14sp"
                android:textStyle="bold" />

            <com.sharedcode.widgets.CustomImageView
                android:id="@+id/exo_fullscreen_btn"
                android:layout_width="24dp"
                android:layout_height="24dp"
                android:layout_margin="8dp"
                android:src="@drawable/ic_fullscreen"
                 />

        </LinearLayout>
    </LinearLayout>
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

Let me know if this works.

like image 2
Harsh Masand Avatar answered Oct 19 '22 16:10

Harsh Masand