如何让ExoPlayer通过Activity的onStart()方法恢复视频? [英] How I get ExoPlayer to resume a video in my Activity's onStart() method?

查看:71
本文介绍了如何让ExoPlayer通过Activity的onStart()方法恢复视频?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用ExoPlayer从URL播放一些mp4.当用户单击主页"按钮或导致该应用程序从用户视图中删除然后又返回到我的应用程序(以及视频活动)的所有内容时,我希望视频从上次停止的地方继续播放.我尝试通过将视频位置保存在 onStop()中,然后重建播放器并在 onStart()中使用 seekTo()来实现此目的.我有一个检查,看看我当前的exoplayer在 onStart()中是否为null,但是此检查从未通过,所以我认为这就是问题所在.现在如何编码,我的视频再也无法恢复.如果我通过按下主页按钮离开应用程序,则会调用 onStop(),然后当我返回应用程序时会调用 onStart(),但是视频播放器仍然存在黑色,并且从不播放视频.如果取消空检查,则每次从主要活动开始播放视频时,都会得到两个正在播放的视频实例,因为在 onCreate() onStart()中都调用了该视频.有没有更好的方法来获取我想要的功能?感谢您的帮助!

I'm using ExoPlayer to play some mp4's from a URL. When the user click's the home button, or anything that causes the app to be removed from the user's view and then comes back to my app (and video activity) I want the video to resume where it left off. I tried implementing this by saving the video position in onStop() and then rebuilding the player and using seekTo() in onStart(). I have a check to see if my current exoplayer is null in onStart(), this check never passes however, so I think this is where the problem lies. How it's coded now my video never resumes. If I leave the app by pressing the home button onStop() gets called and then onStart() will get called when I go back into my app, however the video player remains black and never plays the video. If I remove the null check I get two instances of the video playing whenever I start a video from the main activity because it gets called both in onCreate() and onStart(). Is there a better method for getting the functionality that I want? Any help is appreciated!

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

推荐答案

当前,您正在 onStop()中调用release,这将取消播放器的所有重要部分,但不会取消 exoPlayer 字段(并销毁您不跟踪自己的任何状态).

Currently you're calling release in onStop() which will null out all the important pieces of the player, but not the exoPlayer field (and destroy any state that you aren't keeping track of yourself).

有几种不同的方法.但是,无论您做什么,都可能想要自己跟踪一些状态.在下面,我将它们用作字段,但也可以将它们放置在 onSavedInstanceState()中.在 onStop()中,我们保存了两条信息,然后在 onStart()中将它们拉出.1)暂停时玩家所处的最后位置,以及2)恢复后我们是否应该玩游戏.您可以将 seekTo 调用移出 if == null 块,因为您可能总是想从上次中断的地方继续.

There are a few different approaches. But no matter what you do, you'll likely want to keep track of some state yourself. Below I use them as fields, but they could also be placed in onSavedInstanceState(). In onStop() we're saving off two pieces of information and then pulling them out in onStart(). 1) The last position our player was in when pausing and 2) whether we should play when resumed. You can likely move your seekTo call out of the if == null block since you'll probably always want to resume from where you left off.:

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

    // ...
}

现在,我在您的代码示例中看到的另一个问题是 exoPlayer.release()不会使字段 exoPlayer 无效.因此,您还可以在 exoPlayer.release()之后添加 exoPlayer = null 行,这有望解决您的多个exoPlayers问题.您也可以将 release()调用移至 onDestroy(),但前提是您知道要正确地重新实例化所有内容.

Now the other issue I see with your code sample is that exoPlayer.release() won't null out the field exoPlayer. So you could additionally add the line exoPlayer = null after exoPlayer.release() which should hopefully fix your issue of multiple exoPlayers. You could also move the release() call to onDestroy() but only if you know you're reinstantiating everything correctly.

这篇关于如何让ExoPlayer通过Activity的onStart()方法恢复视频?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆