全屏Exoplayer

时间:2018-01-24 11:07:24

标签: android fullscreen exoplayer

我尝试在 RecyclerView ViewPager 中使用 exoplayer 显示节目视频(.mp4)。我用自定义布局显示视频控制器。到目前为止一切顺利。

现在尝试像其他视频播放器一样全屏显示视频以前如何使用,但无法在 exoplayer doc中找到一个好方法。

任何人都可以帮助我吗?

9 个答案:

答案 0 :(得分:10)

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< = 22,需要创建新的编解码器实例以交换到另一个表面。这会中断播放,这种方法的用户体验会降低。

方法2

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

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

进一步的方法/挑战

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

答案 1 :(得分:9)

您可以通过[General] [<_NSFaultingMutableOrderedSet 0x6000017c2300> addObserver:forKeyPath:options:context:] is not supported. Key path: firstObject.name 的XML轻松地做到这一点。设置以下属性:

ExoPlayer

答案 2 :(得分:0)

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

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

希望有所帮助

答案 3 :(得分:0)

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

  1. 使用此按钮隐藏状态栏: getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

  2. 使用此按钮隐藏导航栏: getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);

答案 4 :(得分:0)

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

默认情况下,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>

在Java中,

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

答案 5 :(得分:0)

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方法,因为在这里我使用了Fragment。所以在这里,我更改了放置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>

让我知道这是否可行。

答案 6 :(得分:0)

如果要使用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")

答案 7 :(得分:0)

要在Expo Player上显示完整的全屏视频,请在xml文件app:resize_mode =“ fill”

中使用此行

答案 8 :(得分:-1)

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