ExoPlayer 在屏幕旋转后恢复到同一位置

22

我在我的活动中使用ExoPlayer,我希望视频可以在纵向和横向模式下流畅播放。为此,我在onpause中保存了当前播放器位置,并在onresume中将播放器定位到该位置,但旋转时会出现抖动,视频会停止一段时间,然后播放到保存的位置。

我的代码如下,请帮我解决如何平稳地切换纵向和横向模式。谢谢。

     @Override
public void onPause() {

    super.onPause();

    if (mExoPlayerView != null && mExoPlayerView.getPlayer() != null) {
        mResumeWindow = mExoPlayerView.getPlayer().getCurrentWindowIndex();
        mResumePosition = Math.max(0, mExoPlayerView.getPlayer().getContentPosition());
        mExoPlayerView.getPlayer().release();
    }
}


@Override
public void onDestroy() {
    super.onDestroy();
    if (mExoPlayerView.getPlayer() != null)
        mExoPlayerView.getPlayer().release();
}


  @Override
public void onSaveInstanceState(Bundle outState) {

    outState.putInt(STATE_RESUME_WINDOW, mResumeWindow);
    outState.putLong(STATE_RESUME_POSITION, mResumePosition);
    outState.putBoolean(STATE_PLAYER_FULLSCREEN, mExoPlayerFullscreen);
    super.onSaveInstanceState(outState);
}



   @Override
protected void onResume() {

    super.onResume();

    if (mExoPlayerView == null) {

        mExoPlayerView = (SimpleExoPlayerView) findViewById(R.id.exoplayer);

        videoURL = getIntent().getStringExtra("url");
        postID = getIntent().getIntExtra("UserID", 0);

        String userAgent = Util.getUserAgent(Vid.this, getApplicationContext().getApplicationInfo().packageName);
        DefaultHttpDataSourceFactory httpDataSourceFactory = new DefaultHttpDataSourceFactory(userAgent, null, DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS, DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS, true);
        DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(Vid.this, null, httpDataSourceFactory);
        Uri daUri = Uri.parse(videoURL);

        ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();

        if (daUri.toString().startsWith("https://player.vimeo"))
            mVideoSource = new HlsMediaSource(daUri, dataSourceFactory, 1, null, null);
        else
            mVideoSource = new ExtractorMediaSource(daUri, dataSourceFactory, extractorsFactory, null, null);

        initExoPlayer();

    } else {
        resumeExoPlayer();
    }

}


   private void resumeExoPlayer() {

    boolean haveResumePosition = mResumeWindow != C.INDEX_UNSET;

    if (haveResumePosition) {
        hideKeyboard();
        hideProgress();
        mExoPlayerView.getPlayer().seekTo(mResumeWindow, mResumePosition);
    }
}

private void initExoPlayer() {
    BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
    TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
    TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
    LoadControl loadControl = new DefaultLoadControl();
    SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(new DefaultRenderersFactory(this), trackSelector, loadControl);
    mExoPlayerView.setPlayer(player);

    boolean haveResumePosition = mResumeWindow != C.INDEX_UNSET;

    if (haveResumePosition) {
        hideKeyboard();
        hideProgress();
        mExoPlayerView.getPlayer().seekTo(mResumeWindow, mResumePosition);

    }

    mExoPlayerView.getPlayer().prepare(mVideoSource);
    mExoPlayerView.getPlayer().setPlayWhenReady(true);

    mExoPlayerView.getPlayer().addListener(new Player.EventListener() {
        @Override
        public void onTimelineChanged(Timeline timeline, Object manifest) {

        }

        @Override
        public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {

        }

        @Override
        public void onLoadingChanged(boolean isLoading) {

        }

        @Override
        public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {

            if (playbackState == ExoPlayer.STATE_ENDED) {
                hideProgress();
                mExoPlayerView.getPlayer().seekTo(0);
                mExoPlayerView.getPlayer().setPlayWhenReady(false);
            } else if (playbackState == ExoPlayer.STATE_BUFFERING) {
            } else if (playbackState == ExoPlayer.STATE_READY) {
                hideProgress();

                if (preferenceManager.getLoggedIn()) {
                    APIGetComment();
                }
            }
        }

        @Override
        public void onRepeatModeChanged(int repeatMode) {

        }

        @Override
        public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {

        }

        @Override
        public void onPlayerError(ExoPlaybackException error) {
            hideProgress();
            finish();
        }

        @Override
        public void onPositionDiscontinuity(int reason) {

        }

        @Override
        public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {

        }

        @Override
        public void onSeekProcessed() {

        }
    });
}

玩家的准备需要时间,你无法做太多事情来加快它。 - Mohammed Atif
4个回答

48

最终,在浪费了2天的时间后,我找到了它。 只需在清单文件中简单添加它,就可以在所有 Android 版本上运行?


android:configChanges="orientation|screenSize|layoutDirection"

干杯!


1
谢谢,是的,它解决了问题。对于像我这样的新手,您需要在“AndroidManifest.xml”中寻找您的视频活动,并将其放置在“<activity...”定义中。 - visual
6
好的,请不要这样做。这种“变通方法”会在您的应用程序中导致许多其他问题,包括在旋转设备或启用分屏时显示错误的布局。 - nhaarman
1
您不需要防止配置更改才能使其正常工作。此解决方案应该有效 https://dev59.com/gCL-s4cB2Jgan1znQ-sZ#62427122 - Gregriggins36

3
无需任何额外的编码,只需添加此行。
android:configChanges="keyboardHidden|orientation|screenSize"

在你的AndroidManifest.xml文件的activity部分。

3
如果您希望视频在屏幕方向改变后恢复播放,可以将以下代码添加到您的清单文件中:android:configChanges="keyboardHidden|orientation|screenSize"
     <activity
     <activity
         android:name=".MainActivity"
         android:name=".MainActivity"
         android:label="@string/app_name"
         android:label="@string/app_name"
+            android:configChanges="keyboardHidden|orientation|screenSize"
         android:theme="@style/AppTheme.NoActionBar"
         android:theme="@style/AppTheme.NoActionBar"
         android:icon="@mipmap/ic_launcher_2">
         android:icon="@mipmap/ic_launcher_2">
         <intent-filter>
         <intent-filter>

0

我也在这方面浪费了很多时间。看一下EXO PLAYER 2.11.2。

    implementation 'com.google.android.exoplayer:exoplayer:2.11.2'

步骤 - 1 创建一个活动,在其中将字符串URL作为意图传递。

  public class VideoPlayerActivity extends Activity {

  public static final String sURL_KEY = "STREAMING_URL_KEY";
  public static final String sTOAST_TEXT = "Unable to stream, no media found";
  static final String LOADING = "PLAYER_LOADING";
  static final String STOPPED = "PLAYER_STOPPED";
  static final String PAUSED = "PLAYER_PAUSED";
  static final String PLAYING = "PLAYER_PLAYING";
  static final String IDLE = "PLAYER_IDLE";
  private static final String TAG = "StreamMediaActivity";
  int orientation;
  private Uri streamUrl;
  private SimpleExoPlayer mPlayer;
  private PlayerView playerView;
  private ProgressBar progressBar;
  private String mPlayerStatus;
  private long mPlaybackPosition = 0L;
  private boolean mIsPlayWhenReady = true;
  private int mCurrentWindow = 0;
  private Display display;
  private String STATE_RESUME_WINDOW = "resumeWindow";
  private String STATE_RESUME_POSITION = "resumePosition";
  private String STATE_PLAYER_FULLSCREEN = "playerFullscreen";
  private boolean mExoPlayerFullscreen = false;

  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    fullScreen();
    display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
    orientation = display.getRotation();
    setContentView(R.layout.activity_video_player);
    playerView = findViewById(R.id.player_view);
    progressBar = findViewById(R.id.progressBar_player);

// Pass a string uri to this class
    String urlString = getIntent().getStringExtra(sURL_KEY);
    if (urlString != null) {
      streamUrl = Uri.parse(urlString);
    } else {
      Toast.makeText(this, sTOAST_TEXT, Toast.LENGTH_LONG).show();
      finish();
    }
  }

  @Override
  protected void onStart() {
    super.onStart();
    initPlayer();
  }

  @Override
  protected void onResume() {
    super.onResume();
    if (mPlaybackPosition != 0L && mPlayer != null) {
      mPlayer.seekTo(mCurrentWindow, mPlaybackPosition);
    }
  }

  @Override
  protected void onStop() {
    super.onStop();
  }

  @Override protected void onPause() {
    super.onPause();
    releasePlayer();
  }



    private void initPlayer() {
        // ESTABLISH THE DATA SOURCE FROM URL
    // here i'm playing local video file that's
 // why using the DefaultDataSourceFactory but you 
//may use DefaultHttpDataSourceFactory to stream 
//online videos 
        DataSource.Factory dataSourceFactory =
            new DefaultDataSourceFactory(this, Util.getUserAgent(this, getApplicationInfo().name));

    MediaSource mediaSource =
        new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(
            streamUrl);


    // CREATE A NEW INSTANCE OF EXO PLAYER
    if (mPlayer == null) {
      mPlayer = new SimpleExoPlayer.Builder(this, new DefaultRenderersFactory(this)).build();
      playerView.setPlayer(mPlayer);
      progressBar.setVisibility(View.VISIBLE);
    }
    mPlayer.setPlayWhenReady(mIsPlayWhenReady);
    mPlayer.seekTo(mCurrentWindow, mPlaybackPosition);

    // PREPARE MEDIA PLAYER
    mPlayer.prepare(mediaSource, true, false);
    mPlayer.addListener(new Player.EventListener() {
      @Override
      public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
        switch (playbackState) {
          case Player.STATE_BUFFERING:
            mPlayerStatus = LOADING;
            runOnUiThread(() -> progressBar.setVisibility(View.VISIBLE));
            break;
          case Player.STATE_ENDED:
            mPlayerStatus = STOPPED;
            break;
          case Player.STATE_READY:
            mPlayerStatus = (playWhenReady) ? PLAYING : PAUSED;
            runOnUiThread(() -> progressBar.setVisibility(View.INVISIBLE));
            break;
          default:
            mPlayerStatus = IDLE;
            break;
        }
      }

      @Override
      public void onPlayerError(ExoPlaybackException error) {
        Toast.makeText(VideoPlayerActivity.this, "Something went wrong", Toast.LENGTH_SHORT).show();
        finish();
      }
    });
  }

  @Override protected void onSaveInstanceState(Bundle outState) {
    mExoPlayerFullscreen = !mExoPlayerFullscreen;
    super.onSaveInstanceState(outState);
    outState.putInt(STATE_RESUME_WINDOW, mCurrentWindow);
    outState.putLong(STATE_RESUME_POSITION, mPlaybackPosition);
    outState.putBoolean(STATE_PLAYER_FULLSCREEN, mExoPlayerFullscreen);
    super.onSaveInstanceState(outState);
  }

  public void fullScreen() {
    View decorView = getWindow().getDecorView();
    decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
  }

  private void releasePlayer() {
    if (mPlayer != null) {
      mPlayer.stop();
      mPlaybackPosition = mPlayer.getCurrentPosition();
      mCurrentWindow = mPlayer.getCurrentWindowIndex();
      mIsPlayWhenReady = mPlayer.getPlayWhenReady();
      playerView.setPlayer(null);
      mPlayer.release();
      mPlayer = null;
    }
  }
}

第二步:制作XML布局

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

  <FrameLayout
      android:layout_width="0dp"
      android:layout_height="0dp"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent">

    <com.google.android.exoplayer2.ui.PlayerView
        android:id="@+id/player_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:keepScreenOn="true"
        app:use_controller="true"
        app:resize_mode="fit"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ProgressBar
        android:id="@+id/progressBar_player"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center" />

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

步骤3:使用来自另一个活动的意图启动VideoPlayerActivity

Intent streamVideoIntent = new Intent(context, VideoPlayerActivity.class);
                        streamVideoIntent.putExtra(sURL_KEY, stringUrl);
                        context.startActivity(streamVideoIntent);

步骤4:最后将活动添加到清单中

<activity android:name=".ui.videoplayer.VideoPlayerActivity"
        android:configChanges="orientation|screenSize|layoutDirection"
        />

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接