Reputation: 2701
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();
}
}
Upvotes: 3
Views: 9343
Reputation: 1
In my case, if i minimize or try to move from the this player video app to another app. The video player app always starting play from 0. I was try and succesfully, the video player app playing video from the last current position when you minimalize or move to another app, and this it.
go to
private void buildPlayer(String mp4Url)
add
player.seekTo(playbackPosition);
and then
@Override
public void onResume() {
super.onResume();
if(playbackPosition!=0 && player!=null){
player.seekTo(playbackPosition);
initializePlayer();
}
}
@Override
public void onPause() {
super.onPause();
player.stop();
if(player != null && player.getPlayWhenReady()) {
player.stop();
playbackPosition = player.getCurrentPosition();
player.setPlayWhenReady(true);
}
}
@Override
public void onStop() {
super.onStop();
player.setPlayWhenReady(false);
player.stop();
player.seekTo(0);
}
And you can try remove the
exoPlayer.setPlayWhenReady(true);
from
private void buildPlayer(String mp4Url)
Upvotes: 0
Reputation: 31
There is no need to reinit player again. The following code is pretty enough:
@Override
protected void onPause() {
super.onPause();
if (player!=null) {
player.stop();
mLastPosition = player.getCurrentPosition();
}
}
@Override
protected void onResume() {
super.onResume();
//initiatePlayer();
if(mLastPosition!=0 && player!=null){
player.seekTo(mLastPosition);
}
}
Upvotes: 3
Reputation: 23
Try the following
initiate the palyer again in onResume and set seek to the last position of the player
private void initiatePlayer() {
try {
exoPlayer = ExoPlayerFactory.newSimpleInstance(this);
DataSource.Factory dataSourceFactory =
new DefaultDataSourceFactory(this, Util.getUserAgent(this, this.getResources().getString(R.string.app_name)));
DefaultExtractorsFactory extractorsFactory =
new DefaultExtractorsFactory()
.setMp4ExtractorFlags(Mp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS);
ProgressiveMediaSource progressiveMediaSource =
new ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory)
.createMediaSource(videoUri);
// playerView = new PlayerView(this);
playerView.setPlayer(exoPlayer);
exoPlayer.prepare(progressiveMediaSource);
exoPlayer.setPlayWhenReady(true);
PlayerControlView controlView = playerView.findViewById(R.id.exo_controller);
mFullScreenIcon = controlView.findViewById(R.id.exo_fullscreen_icon);
ImageView volumeControl = controlView.findViewById(R.id.exo_volume);
mFullScreenIcon.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(ORIENTATION ==Configuration.ORIENTATION_LANDSCAPE){
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
Timer mRestoreOrientation = new Timer();
mRestoreOrientation.schedule(new TimerTask() {
@Override
public void run() {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}
}, 2000);
}
});
volumeControl.setOnClickListener(view -> {
float currentvolume = exoPlayer.getVolume();
if (currentvolume > 0f) {
previousVolume = currentvolume;
exoPlayer.setVolume(0f);
volumeControl.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_volume_off_white_24dp));
} else {
exoPlayer.setVolume(previousVolume);
volumeControl.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_volume_up_white_24dp));
}
});
} catch (Exception e) {
e.printStackTrace();
Log.d("MainExcep", e.getMessage());
}
}
@Override
protected void onPause() {
super.onPause();
if (exoPlayer!=null) {
exoPlayer.stop();
mLastPosition = exoPlayer.getCurrentPosition();
}
}
@Override
protected void onResume() {
super.onResume();
initiatePlayer();
if(mLastPosition!=0){
exoPlayer.seekTo(mLastPosition);
}
}
Upvotes: 0
Reputation: 8078
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).
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);
// ...
}
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.
Upvotes: 6