如何让 ExoPlayer 在 Activity 的 onStart() 方法中恢复视频?

int*_*ntA 3 video android android-activity exoplayer

我正在使用 ExoPlayer 从 URL 播放一些 mp4。当用户单击主页按钮或任何导致应用程序从用户视图中删除然后返回到我的应用程序(和视频活动)时,我希望视频从中断处恢复。我尝试通过将视频位置保存在中onStop(),然后重建播放器并使用seekTo()in 来实现此目的onStart()。我有一个检查来查看我当前的 exoplayer 中是否为空onStart(),但是该检查从未通过,所以我认为这就是问题所在。现在的编码方式我的视频永远不会恢复。如果我通过按主页按钮离开应用程序,onStop()则会被呼叫,然后onStart()当我返回应用程序时也会被呼叫,但是视频播放器保持黑色并且从不播放视频。如果我删除空检查,每当我从主要活动启动视频时,我都会得到两个正在播放的视频实例,因为它会在onCreate()和 中被调用onStart()。有没有更好的方法来获得我想要的功能?任何帮助表示赞赏!

public class VideoActivity extends AppCompatActivity {

    private SimpleExoPlayer exoPlayer;
    private SimpleExoPlayerView simpleExoPlayerView;
    private long playerPosition;
    private String mp4Url;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.player_activity);

        // Get the Intent that started this activity and extract the video url
        Intent intent = getIntent();
        mp4Url = intent.getStringExtra(MainActivity.VIDEO_URL);
        // Create an exoplayer instance and start playing video
        buildPlayer(mp4Url);

    }

    private void buildPlayer(String mp4Url) {
        // Create a default TrackSelector
        Handler mainHandler = new Handler();
        BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
        TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
        TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);

        // Create the player
        exoPlayer = ExoPlayerFactory.newSimpleInstance(this, trackSelector); // no LoadControl?
        simpleExoPlayerView = new SimpleExoPlayerView(this);
        simpleExoPlayerView = (SimpleExoPlayerView) findViewById(R.id.player_view);

        // Set media controller
        simpleExoPlayerView.setUseController(true);
        simpleExoPlayerView.requestFocus();

        // Bind player to the view
        simpleExoPlayerView.setPlayer(exoPlayer);

        // Create Uri from video location
        // TODO: should this be in some network class? Should I be appending APIKEY here?
        Uri mp4Uri = Uri.parse(mp4Url + "?api_key=" + BuildConfig.GIANTBOMB_API_KEY);
        Timber.v("Video url with api key: " + mp4Uri.toString());

        // Create another bandwidth meter for bandwidth during playback (not strictly necessary)
        DefaultBandwidthMeter playbackBandwidthMeter = new DefaultBandwidthMeter();

        // DataSourceFactory to produce DataSource instances through which media data is loaded
        DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(this,
                Util.getUserAgent(this, "GiantBombForAndroid"),
                playbackBandwidthMeter);

        // Produces Extractor instances for parsing the media data
        ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();

        ExtractorMediaSource.EventListener eventListener = new ExtractorMediaSource.EventListener() {
            @Override
            public void onLoadError(IOException error) {
                Timber.e("Error loading video from source");
            }
        };

        final MediaSource videoSource = new ExtractorMediaSource(mp4Uri,
                dataSourceFactory,
                extractorsFactory,
                mainHandler,
                eventListener);

        exoPlayer.prepare(videoSource);

        exoPlayer.addListener(new ExoPlayer.EventListener() {
            @Override
            public void onLoadingChanged(boolean isLoading) {
                Timber.v("Listener-onLoadingChanged...");

            }

            @Override
            public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
                Timber.v("Listener-onPlayerStateChanged...");

            }

            @Override
            public void onTimelineChanged(Timeline timeline, Object manifest) {
                Timber.v("Listener-onTimelineChanged...");

            }

            @Override
            public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
                // TODO: Do I need anything here?
            }

            @Override
            public void onPlayerError(ExoPlaybackException error) {
                Timber.v("Listener-onPlayerError...");
                exoPlayer.stop();
                exoPlayer.prepare(videoSource);
                exoPlayer.setPlayWhenReady(true);
            }

            @Override
            public void onPositionDiscontinuity() {
                Timber.v("Listener-onPositionDiscontinuity...");

            }

            @Override
            public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
                // TODO: Do I need anything here?
            }
        });
        exoPlayer.setPlayWhenReady(true);
    }

    @Override
    protected void onStart() {
        super.onStart();
        Timber.v("onStart()...");
        if (exoPlayer == null) {
            Timber.v("No exoplayer instance, recreating...");
            buildPlayer(mp4Url);
            exoPlayer.seekTo(playerPosition);
        }
    }

    @Override
    protected void onStop(){
        super.onStop();
        Timber.v("onStop()...");
        //TODO: pull player creation code into it's own method so it can be called here as well
        playerPosition = exoPlayer.getCurrentPosition();
        exoPlayer.release();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Timber.v("onDestroy()...");
        exoPlayer.release();
    }
}
Run Code Online (Sandbox Code Playgroud)

Kyl*_*enn 6

目前,您正在调用释放,其中onStop()将清除玩家的所有重要部分,但不会exoPlayer清除字段(并销毁您没有跟踪自己的任何状态)。

有几种不同的方法。但无论您做什么,您可能都希望自己跟踪某些状态。下面我将它们用作字段,但它们也可以放置在onSavedInstanceState(). 在中onStop(),我们保存了两条信息,然后将它们拉出onStart()。1) 暂停时我们的球员所处的最后位置,2) 恢复时我们是否应该继续比赛。您可以将seekTo呼叫移出该if == null区域,因为您可能总是希望从上次中断的位置继续。:

@Override
public void onStart() {
    // ...

    if (exoPlayer == null) {
        // init player
    }

    // Seek to the last position of the player.
    exoPlayer.seekTo(mLastPosition);

    // Put the player into the last state we were in.
    exoPlayer.setPlayWhenReady(mPlayVideoWhenForegrounded);

    // ...
}

@Override
public void onStop() {
    // ...

    // Store off if we were playing so we know if we should start when we're foregrounded again.
    mPlayVideoWhenForegrounded = exoPlayer.getPlayWhenReady();

    // Store off the last position our player was in before we paused it.
    mLastPosition = exoPlayer.getCurrentPosition();

    // Pause the player
    exoPlayer.setPlayWhenReady(false);

    // ...
}
Run Code Online (Sandbox Code Playgroud)

现在我在您的代码示例中看到的另一个问题是exoPlayer.release()不会使该字段为空exoPlayerexoPlayer = null因此,您可以另外添加以下行exoPlayer.release(),希望可以解决多个 exoPlayers 的问题。您也可以将release()调用移至onDestroy(),但前提是您知道自己正在正确地重新实例化所有内容。