【问题标题】:Fullscreen the Exoplayer全屏 Exoplayer
【发布时间】:2018-07-03 09:49:35
【问题描述】:

我尝试在 RecyclerViewViewPager 中显示带有 exoplayer 的节目视频 (.mp4)。我展示了具有自定义布局的视频控制器。到目前为止一切顺利。

现在尝试像其他视频播放器一样全屏播放视频,但在 exoplayer 文档中找不到好方法。

谁能帮帮我?

【问题讨论】:

  • 我已经解释了如何在stackoverflow.com/a/58219056/4034572 中使用新的全屏 Activity 来实现这一点。该解决方案非常简单且运行良好,但缺点是需要再次下载视频片段,从而停止播放。我还展示了一个使用PlayerView.switchTargetView 来避免这种情况的替代解决方案。

标签: android fullscreen exoplayer


【解决方案1】:

ExoPlayer 库目前不提供启用/禁用全屏模式的内置方法。恐怕您需要自己实现或为此找到一些第三方代码。

基本上需要两个步骤

a) 将窗口和活动属性设置为全屏和/或沉浸模式,并(如果需要)更改为横向模式。这并不难。见this page on Android Developers

b) 将渲染过渡到SimpleExoPlayerView(实际上是关于 Surface),它在沉浸式模式下覆盖整个视口。这对于在所有 API 级别上实现最佳用户体验来说是一个更大的挑战。

为了获得最佳用户体验,我们希望在切换到全屏和返回时让播放器继续播放,以继续无缝播放。在 RecyclerView 中,解决所有 API 级别的问题有点棘手。

方法 1

最简单的方法可能是有一个单独的 SimpleExoPlayerView 实例,一旦您进入沉浸式模式,您就将它放在布局的顶部(有些人为此打开带有第二个视图的对话框,有些人只是在顶部有第二个视图以某种方式在布局中显示/隐藏它)。

然后,您将播放器实例从嵌入在 RV 中的 SimpleExoPlayerView 中分离出来,并通过调用 static helper method 将其附加到全屏视图:

SimpleExoPlayerView.switchTargetView(simpleExoPlayer, oldPlayerView, newPlayerView);

这种方法在 API >=23 上效果很好。在 API 23 中,添加了允许动态交换表面的方法 MediaCodec.setOutputSurface。上面的静态方法确保应用了这种技术。结果音频和视频继续播放,进入和退出全屏的用户体验非常流畅。对于 API

方法 2

为避免切换到较低 API 级别的另一个界面,您需要使用单个界面并以某种方式将其转换为全屏。您可以隐藏除 SimpleExoPlayerView 之外的所有其他内容并设置布局宽度和高度以匹配其父级,或者您可以将视频视图替换为占位符并将其放在顶部和后面。

这对于简单的布局可以很好地工作,但对于复杂的布局,可能包括片段、viewpagers、recyclerviews,这可能是一个非常侵入性的操作,导致某些东西闪烁或很快中断播放(在某些 API 级别上,例如从视图层次结构)。我已经看到这适用于各种布局。

进一步的方法/挑战

当您深入挖掘和/或根本不使用 SimpleExoPlayerView 时,可能还有其他更好的方法。

【讨论】:

  • SimpleExoPlayerView.switchTargetView 已弃用。您现在应该使用PlayerView.switchTargetView
  • 我使用了第一种方法,它奏效了。我认为这应该是正确的答案。
【解决方案2】:

您可以通过ExoPlayer 的 XML 轻松完成此操作。设置以下属性:

app:resize_mode="fill"

【讨论】:

  • 我不再编码 android 但我希望是正确的并帮助社区。在这种情况下,您将收到一个正确的勾号。
  • 这并没有回答最初的问题,即如何使视频全屏/沉浸式。所有这一切都是应用填充调整大小,以便任何流式视频适合视图。如果您的视图在回收站中,它将被限制为列表项的大小。
【解决方案3】:

Exoplayer 不提供全屏,所以这是对我有用的解决方法。 在这里,我限制了屏幕旋转并手动更改方向以编程方式更改 player_view 的宽度和高度,并设置工具栏的可见性消失。 使用数据绑定和 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)

}

最后是 player_controller 的 XML

<?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>

让我知道这是否有效。

【讨论】:

    【解决方案4】:

    如果你想要一个 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")
    

    【讨论】:

    • 不断改进您的 github 存储库。挺好的
    【解决方案5】:

    确保将播放器视图的长度和宽度设置为与其父视图匹配。

    1. 使用它来隐藏状态栏: getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

    2. 使用它来隐藏导航栏: getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);

    【讨论】:

      【解决方案6】:

      要在 expo 播放器上显示完整的全屏视频,请在 xml 文件 app:resize_mode="fill" 中使用这一行

      【讨论】:

      • 当我将纵向高度设置为 700 像素时,我无法使用您建议的解决方案实现横向全屏模式。当我将高度设置为 match_parent 时,我能够在横向中实现全屏,但我想在纵向时显示 700px 的高度。有什么帮助吗?
      【解决方案7】:

      您可以使用 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
              }
         }
      });
      

      【讨论】:

        【解决方案8】:

        您可以通过设置播放器的参数将播放器设置为全屏。

                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();
        

        【讨论】:

          【解决方案9】:

          这可能为时已晚,但这可能会对其他开发人员有所帮助。

          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();
                      }
                  }
              });
          

          这里我们初始化了out 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>
          

          在java中,

          playerView = (PlayerView) findViewById(R.id.player_view);
          

          【讨论】:

          • 请给出完整的例子,什么是mFullScreenDialog?
          猜你喜欢
          • 2022-06-29
          • 1970-01-01
          • 1970-01-01
          • 2018-08-05
          • 1970-01-01
          • 2023-01-16
          • 1970-01-01
          • 1970-01-01
          • 2018-08-24
          相关资源
          最近更新 更多