Полноэкранный Экзоплеер

Я пытаюсь показать шоу видео (.mp4) с exoplayer в RecyclerView и ViewPager. Я показываю видео контроллер с пользовательским макетом. Все идет нормально.

Теперь попробуйте использовать полноэкранный режим видео, как это было раньше, но не можете найти хороший путь в документе exoplayer.

Может кто-нибудь мне помочь?

11 ответов

Библиотека ExoPlayer в настоящее время не предоставляет встроенного способа включения / отключения полноэкранного режима. Вам нужно реализовать это самостоятельно или найти какой-то сторонний код для этого, я боюсь.

По сути, требуется два шага:

a) Установите свойства окна и действия в полноэкранный и / или иммерсивный режим и (при желании) перейдите в ландшафтный режим. Это не сложно. Смотрите эту страницу на Android разработчиков.

б) Переход от рендеринга к SimpleExoPlayerView (на самом деле речь идет о поверхности), которая охватывает весь видовой экран в режиме погружения. Это более сложная задача для достижения оптимального взаимодействия с пользователем на всех уровнях API.

Для оптимального взаимодействия с пользователем мы хотим, чтобы проигрыватель воспроизводился во время перехода к полноэкранному режиму и обратно, чтобы воспроизведение продолжалось без проблем. В RecyclerView это довольно сложно решить для всех уровней API.

Подход 1

Самым простым способом, вероятно, является наличие отдельного экземпляра SimpleExoPlayerView, который вы помещаете поверх макета, как только вы входите в режим погружения (некоторые люди открывают для этого диалоговое окно со вторым видом, у некоторых второй макет отображается просто сверху в макете). как-нибудь и показать / скрыть это по требованию).

Затем вы отсоединяете экземпляр проигрывателя от SimpleExoPlayerView, встроенного в RV, и присоединяете его к полноэкранному представлению с помощью вызова статического вспомогательного метода:

SimpleExoPlayerView.switchTargetView(simpleExoPlayer, oldPlayerView, newPlayerView);

Этот подход очень хорошо работает на API >=23. С API 23 был добавлен метод MediaCodec.setOutputSurface, позволяющий динамически менять поверхности. Статический метод выше гарантирует, что этот метод применяется. В результате аудио и видео продолжают воспроизводиться, и пользовательский опыт от входа и выхода из полноэкранного режима очень плавный. Для API <=22 необходимо создать новый экземпляр кодека, чтобы перейти на другую поверхность. Это прерывает воспроизведение, и пользовательский опыт для этого подхода ухудшается.

Подход 2

Чтобы избежать перехода на другую поверхность на более низком уровне API, необходимо использовать одну поверхность и каким-то образом перевести ее в полноэкранный режим. Вы можете либо просто скрыть все остальное, чем SimpleExoPlayerView и установить ширину и высоту макета, чтобы они соответствовали его родительскому элементу, либо вы можете заменить вид видео заполнителем и поместить его сверху и сзади.

Это может работать довольно хорошо для простых макетов, но со сложными макетами, возможно, включающими фрагменты, пейджеры, просмотры реселлера, это может быть довольно навязчивой операцией, вызывающей мерцание или кратковременное прерывание воспроизведения (на некоторых уровнях API, например, при удалении вида проигрывателя из иерархии представления).). Я видел это работает хорошо для различных макетов, хотя.

Дальнейшие подходы / проблемы

Могут быть другие и, возможно, лучшие подходы, когда вы копаете глубже и / или если вы вообще не используете SimpleExoPlayerView.

Вы можете легко сделать это из XML-файла ExoPlayer. Установите следующий атрибут:

app:resize_mode="fill"

Чтобы показать полное полноэкранное видео на expo player, используйте эту строку в XML-файле app:resize_mode="fill"

Вы можете использовать эту библиотеку, если хотите FullScreen Exoplayer:

https://github.com/Norulab/android-exoplayer-fullscreen

Эта библиотека содержит некоторые функции расширения для ExoPlayer:

val player = SimpleExoPlayer.Builder(context).build()
player.preparePlayer(playerView)
player.setSource(applicationContext, "http://html5videoformatconverter.com/data/images/happyfit2.mp4")

Вам нужно просто добавить прослушиватель в ваш exoplayer, если вы сделаете это в Create, а затем добавить эту полосу =binding.exoplayerView.setControllerOnFullScreenModeChangedListener {}

при этом появится кнопка exoplayer, не забудьте указать версию exoplayer Version или выше = '2.16.1'

Exoplayer не поддерживает полноэкранный режим, поэтому вот обходной путь, который сработал для меня. Здесь я ограничил поворот экрана и вручную изменил ориентацию, программно изменив ширину и высоту player_view, а также убрал видимость панели инструментов. Используется Data Binding и Kotlin.

Следует за блоком кода для 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>

Изменения в манифесте Android

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

Проверьте ориентацию и поверните ее с помощью следующего кода

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

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

        }

    }

Методы подписи следующие

/**
 * 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
}

Теперь просто переопределите метод onConfigurationChanged в своем фрагменте / действии, поскольку здесь я использовал фрагмент. Итак, здесь я изменил ширину / высоту родительского контейнера, в котором находится ExoplayerView.

/**
 * 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)

}

Наконец, XML для 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>

Сообщите мне, работает ли это.

добавьте следующую строку в приложение StyledPlayerView: resize_mode="fill"

после добавления строки ваш xml должен выглядеть как код ниже

      <com.google.android.exoplayer2.ui.StyledPlayerView
  android:id="@+id/player"
  android:layout_width="match_parent"
  android:layout_height="160"
  app:resize_mode="fill"
  app:controller_layout_id="@layout/custom_controller_exoplayer"
  app:show_buffering="when_playing"
  app:show_shuffle_button="true" />

Обязательно установите длину и ширину вида игрока, чтобы он соответствовал его родителю.

  1. Используйте это, чтобы скрыть строку состояния:getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

  2. Используйте это, чтобы скрыть панель навигации:getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);

Вы можете настроить проигрыватель на полный экран, установив параметры проигрывателя.

        Params params = (LinearLayout.LayoutParams) 
exoPlayerView.getLayoutParams();
        params.width=params.MATCH_PARENT;
        params.height=params.MATCH_PARENT;
        exoPlayerView.setLayoutParams(params);
    }

И скрыть панель действий:

 getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
 getActionBar().hide();

надеюсь, это поможет

Вы можете применить магию с помощью ExoPlayerView

playerView.setControllerVisibilityListener(new 
PlaybackControlView.VisibilityListener() {
   @Override
   public void onVisibilityChange(int i) {
      // Using Activity
      if (getActionBar() != null)
          if (i == 0) { 
            // code for show
          } else{
            // code for hide
        }
   }
});

Это может быть слишком поздно, но это может помочь другим разработчикам.

Exoplayer не предоставляет полноэкранный режим по умолчанию. Поэтому нам нужен обходной путь для этого. Мы можем реализовать эту полноэкранную функциональность, используя диалог. Нам просто нужно удалить и добавить Playerview из нашей деятельности в диалог.

mFullScreenDialog = new Dialog(mContext, android.R.style.Theme_Black_NoTitleBar_Fullscreen) {
        public void onBackPressed() {
            if (mExoPlayerFullscreen) {   //mExoPlayerFullscreen is a boolean that we need to maintain to know whether screen is fullscreen or not.
                ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
                closeFullscreenDialog();
            }
            super.onBackPressed();
        }
    };

Здесь мы инициализировали диалог.

mFullScreenButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (!mExoPlayerFullscreen) {
                ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
                openFullscreenDialog();
            } else {
                ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
                closeFullscreenDialog();
            }
        }
    });

Здесь мы инициализировали полноэкранную кнопку нашего exoplayer.

private void openFullscreenDialog() {
    ((ViewGroup) playerView.getParent()).removeView(playerView);
    mFullScreenDialog.addContentView(playerView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
    mFullScreenIcon.setImageDrawable(ContextCompat.getDrawable(mContext, R.drawable.exoplayer_shrink));
    mExoPlayerFullscreen = true;
    mFullScreenDialog.show();
}

private void closeFullscreenDialog() {
    ((ViewGroup) playerView.getParent()).removeView(playerView);
    ((FrameLayout) findViewById(R.id.main_media_frame)).addView(playerView);
    mExoPlayerFullscreen = false;
    mFullScreenDialog.dismiss();
    mFullScreenIcon.setImageDrawable(ContextCompat.getDrawable(mContext, R.drawable.exoplayer_expand));
}

playerview - это Playerview, который вы инициализируете для

<FrameLayout
    android:id="@+id/main_media_frame"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000000">

    <com.google.android.exoplayer2.ui.PlayerView
        android:id="@+id/player_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:focusable="true"></com.google.android.exoplayer2.ui.PlayerView>
</FrameLayout>

и в Яве,

playerView = (PlayerView) findViewById(R.id.player_view);
Другие вопросы по тегам