Merge pull request #3961 from ByteHamster/show-buffering-indicator

Show buffering indicator on ExoPlayer
This commit is contained in:
H. Lehmann 2020-03-25 18:20:42 +01:00 committed by GitHub
commit ae906de06d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 54 additions and 85 deletions

View File

@ -4,7 +4,6 @@ import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.util.Log; import android.util.Log;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DefaultLoadControl; import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.DefaultRenderersFactory; import com.google.android.exoplayer2.DefaultRenderersFactory;
@ -14,55 +13,51 @@ import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SeekParameters; import com.google.android.exoplayer2.SeekParameters;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.ProgressiveMediaSource; import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource; import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.playback.IPlayer;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposable;
import org.antennapod.audio.MediaPlayer; import org.antennapod.audio.MediaPlayer;
import de.danoeh.antennapod.core.util.playback.IPlayer;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class ExoPlayerWrapper implements IPlayer { public class ExoPlayerWrapper implements IPlayer {
private static final String TAG = "ExoPlayerWrapper"; private static final String TAG = "ExoPlayerWrapper";
public static final int ERROR_CODE_OFFSET = 1000; public static final int ERROR_CODE_OFFSET = 1000;
private final Context mContext; private final Context context;
private final Disposable bufferingUpdateDisposable; private final Disposable bufferingUpdateDisposable;
private SimpleExoPlayer mExoPlayer; private SimpleExoPlayer exoPlayer;
private MediaSource mediaSource; private MediaSource mediaSource;
private MediaPlayer.OnSeekCompleteListener audioSeekCompleteListener; private MediaPlayer.OnSeekCompleteListener audioSeekCompleteListener;
private MediaPlayer.OnCompletionListener audioCompletionListener; private MediaPlayer.OnCompletionListener audioCompletionListener;
private MediaPlayer.OnErrorListener audioErrorListener; private MediaPlayer.OnErrorListener audioErrorListener;
private MediaPlayer.OnBufferingUpdateListener bufferingUpdateListener; private MediaPlayer.OnBufferingUpdateListener bufferingUpdateListener;
private PlaybackParameters playbackParameters; private PlaybackParameters playbackParameters;
private MediaPlayer.OnInfoListener infoListener;
ExoPlayerWrapper(Context context) { ExoPlayerWrapper(Context context) {
mContext = context; this.context = context;
mExoPlayer = createPlayer(); exoPlayer = createPlayer();
playbackParameters = mExoPlayer.getPlaybackParameters(); playbackParameters = exoPlayer.getPlaybackParameters();
bufferingUpdateDisposable = Observable.interval(2, TimeUnit.SECONDS) bufferingUpdateDisposable = Observable.interval(2, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(tickNumber -> { .subscribe(tickNumber -> {
if (bufferingUpdateListener != null) { if (bufferingUpdateListener != null) {
bufferingUpdateListener.onBufferingUpdate(null, mExoPlayer.getBufferedPercentage()); bufferingUpdateListener.onBufferingUpdate(null, exoPlayer.getBufferedPercentage());
} }
}); });
} }
private SimpleExoPlayer createPlayer() { private SimpleExoPlayer createPlayer() {
@ -71,42 +66,21 @@ public class ExoPlayerWrapper implements IPlayer {
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS, DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS,
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS); DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS);
loadControl.setBackBuffer(UserPreferences.getRewindSecs() * 1000 + 500, true); loadControl.setBackBuffer(UserPreferences.getRewindSecs() * 1000 + 500, true);
SimpleExoPlayer p = ExoPlayerFactory.newSimpleInstance(mContext, new DefaultRenderersFactory(mContext), SimpleExoPlayer p = ExoPlayerFactory.newSimpleInstance(context, new DefaultRenderersFactory(context),
new DefaultTrackSelector(), loadControl.createDefaultLoadControl()); new DefaultTrackSelector(), loadControl.createDefaultLoadControl());
p.setSeekParameters(SeekParameters.EXACT); p.setSeekParameters(SeekParameters.EXACT);
p.addListener(new Player.EventListener() { p.addListener(new Player.EventListener() {
@Override
public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
}
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
}
@Override
public void onLoadingChanged(boolean isLoading) {
}
@Override @Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
if (playbackState == Player.STATE_ENDED) { if (audioCompletionListener != null && playbackState == Player.STATE_ENDED) {
audioCompletionListener.onCompletion(null); audioCompletionListener.onCompletion(null);
} else if (infoListener != null && playbackState == Player.STATE_BUFFERING) {
infoListener.onInfo(null, android.media.MediaPlayer.MEDIA_INFO_BUFFERING_START, 0);
} else if (infoListener != null) {
infoListener.onInfo(null, android.media.MediaPlayer.MEDIA_INFO_BUFFERING_END, 0);
} }
} }
@Override
public void onRepeatModeChanged(int repeatMode) {
}
@Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
}
@Override @Override
public void onPlayerError(ExoPlaybackException error) { public void onPlayerError(ExoPlaybackException error) {
if (audioErrorListener != null) { if (audioErrorListener != null) {
@ -114,16 +88,6 @@ public class ExoPlayerWrapper implements IPlayer {
} }
} }
@Override
public void onPositionDiscontinuity(int reason) {
}
@Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
}
@Override @Override
public void onSeekProcessed() { public void onSeekProcessed() {
audioSeekCompleteListener.onSeekComplete(null); audioSeekCompleteListener.onSeekComplete(null);
@ -144,7 +108,7 @@ public class ExoPlayerWrapper implements IPlayer {
@Override @Override
public int getCurrentPosition() { public int getCurrentPosition() {
return (int) mExoPlayer.getCurrentPosition(); return (int) exoPlayer.getCurrentPosition();
} }
@Override @Override
@ -154,32 +118,32 @@ public class ExoPlayerWrapper implements IPlayer {
@Override @Override
public int getDuration() { public int getDuration() {
if (mExoPlayer.getDuration() == C.TIME_UNSET) { if (exoPlayer.getDuration() == C.TIME_UNSET) {
return PlaybackServiceMediaPlayer.INVALID_TIME; return PlaybackServiceMediaPlayer.INVALID_TIME;
} }
return (int) mExoPlayer.getDuration(); return (int) exoPlayer.getDuration();
} }
@Override @Override
public boolean isPlaying() { public boolean isPlaying() {
return mExoPlayer.getPlayWhenReady(); return exoPlayer.getPlayWhenReady();
} }
@Override @Override
public void pause() { public void pause() {
mExoPlayer.setPlayWhenReady(false); exoPlayer.setPlayWhenReady(false);
} }
@Override @Override
public void prepare() throws IllegalStateException { public void prepare() throws IllegalStateException {
mExoPlayer.prepare(mediaSource, false, true); exoPlayer.prepare(mediaSource, false, true);
} }
@Override @Override
public void release() { public void release() {
bufferingUpdateDisposable.dispose(); bufferingUpdateDisposable.dispose();
if (mExoPlayer != null) { if (exoPlayer != null) {
mExoPlayer.release(); exoPlayer.release();
} }
audioSeekCompleteListener = null; audioSeekCompleteListener = null;
audioCompletionListener = null; audioCompletionListener = null;
@ -189,35 +153,35 @@ public class ExoPlayerWrapper implements IPlayer {
@Override @Override
public void reset() { public void reset() {
mExoPlayer.release(); exoPlayer.release();
mExoPlayer = createPlayer(); exoPlayer = createPlayer();
} }
@Override @Override
public void seekTo(int i) throws IllegalStateException { public void seekTo(int i) throws IllegalStateException {
mExoPlayer.seekTo(i); exoPlayer.seekTo(i);
audioSeekCompleteListener.onSeekComplete(null); audioSeekCompleteListener.onSeekComplete(null);
} }
@Override @Override
public void setAudioStreamType(int i) { public void setAudioStreamType(int i) {
AudioAttributes a = mExoPlayer.getAudioAttributes(); AudioAttributes a = exoPlayer.getAudioAttributes();
AudioAttributes.Builder b = new AudioAttributes.Builder(); AudioAttributes.Builder b = new AudioAttributes.Builder();
b.setContentType(i); b.setContentType(i);
b.setFlags(a.flags); b.setFlags(a.flags);
b.setUsage(a.usage); b.setUsage(a.usage);
mExoPlayer.setAudioAttributes(b.build()); exoPlayer.setAudioAttributes(b.build());
} }
@Override @Override
public void setDataSource(String s) throws IllegalArgumentException, IllegalStateException { public void setDataSource(String s) throws IllegalArgumentException, IllegalStateException {
Log.d(TAG, "setDataSource: " + s); Log.d(TAG, "setDataSource: " + s);
DefaultHttpDataSourceFactory httpDataSourceFactory = new DefaultHttpDataSourceFactory( DefaultHttpDataSourceFactory httpDataSourceFactory = new DefaultHttpDataSourceFactory(
Util.getUserAgent(mContext, mContext.getPackageName()), null, Util.getUserAgent(context, context.getPackageName()), null,
DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS, DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS,
DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS, DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS,
true); true);
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(mContext, null, httpDataSourceFactory); DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(context, null, httpDataSourceFactory);
DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory(); DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
extractorsFactory.setConstantBitrateSeekingEnabled(true); extractorsFactory.setConstantBitrateSeekingEnabled(true);
ProgressiveMediaSource.Factory f = new ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory); ProgressiveMediaSource.Factory f = new ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory);
@ -226,13 +190,13 @@ public class ExoPlayerWrapper implements IPlayer {
@Override @Override
public void setDisplay(SurfaceHolder sh) { public void setDisplay(SurfaceHolder sh) {
mExoPlayer.setVideoSurfaceHolder(sh); exoPlayer.setVideoSurfaceHolder(sh);
} }
@Override @Override
public void setPlaybackParams(float speed, boolean skipSilence) { public void setPlaybackParams(float speed, boolean skipSilence) {
playbackParameters = new PlaybackParameters(speed, playbackParameters.pitch, skipSilence); playbackParameters = new PlaybackParameters(speed, playbackParameters.pitch, skipSilence);
mExoPlayer.setPlaybackParameters(playbackParameters); exoPlayer.setPlaybackParameters(playbackParameters);
} }
@Override @Override
@ -242,7 +206,7 @@ public class ExoPlayerWrapper implements IPlayer {
@Override @Override
public void setVolume(float v, float v1) { public void setVolume(float v, float v1) {
mExoPlayer.setVolume(v); exoPlayer.setVolume(v);
} }
@Override @Override
@ -252,14 +216,14 @@ public class ExoPlayerWrapper implements IPlayer {
@Override @Override
public void start() { public void start() {
mExoPlayer.setPlayWhenReady(true); exoPlayer.setPlayWhenReady(true);
// Can't set params when paused - so always set it on start in case they changed // Can't set params when paused - so always set it on start in case they changed
mExoPlayer.setPlaybackParameters(playbackParameters); exoPlayer.setPlaybackParameters(playbackParameters);
} }
@Override @Override
public void stop() { public void stop() {
mExoPlayer.stop(); exoPlayer.stop();
} }
void setOnCompletionListener(MediaPlayer.OnCompletionListener audioCompletionListener) { void setOnCompletionListener(MediaPlayer.OnCompletionListener audioCompletionListener) {
@ -275,20 +239,24 @@ public class ExoPlayerWrapper implements IPlayer {
} }
int getVideoWidth() { int getVideoWidth() {
if (mExoPlayer.getVideoFormat() == null) { if (exoPlayer.getVideoFormat() == null) {
return 0; return 0;
} }
return mExoPlayer.getVideoFormat().width; return exoPlayer.getVideoFormat().width;
} }
int getVideoHeight() { int getVideoHeight() {
if (mExoPlayer.getVideoFormat() == null) { if (exoPlayer.getVideoFormat() == null) {
return 0; return 0;
} }
return mExoPlayer.getVideoFormat().height; return exoPlayer.getVideoFormat().height;
} }
void setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener bufferingUpdateListener) { void setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener bufferingUpdateListener) {
this.bufferingUpdateListener = bufferingUpdateListener; this.bufferingUpdateListener = bufferingUpdateListener;
} }
public void setOnInfoListener(MediaPlayer.OnInfoListener infoListener) {
this.infoListener = infoListener;
}
} }

View File

@ -1042,6 +1042,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
ap.setOnSeekCompleteListener(audioSeekCompleteListener); ap.setOnSeekCompleteListener(audioSeekCompleteListener);
ap.setOnBufferingUpdateListener(audioBufferingUpdateListener); ap.setOnBufferingUpdateListener(audioBufferingUpdateListener);
ap.setOnErrorListener(audioErrorListener); ap.setOnErrorListener(audioErrorListener);
ap.setOnInfoListener(audioInfoListener);
} else { } else {
Log.w(TAG, "Unknown media player: " + mp); Log.w(TAG, "Unknown media player: " + mp);
} }

View File

@ -291,13 +291,9 @@ public class PlaybackController {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
if (!isConnectedToPlaybackService()) { int type = intent.getIntExtra(PlaybackService.EXTRA_NOTIFICATION_TYPE, -1);
bindToService(); int code = intent.getIntExtra(PlaybackService.EXTRA_NOTIFICATION_CODE, -1);
return; if (code == -1 || type == -1) {
}
int type = intent.getIntExtra(PlaybackService.EXTRA_NOTIFICATION_TYPE, -1);
int code = intent.getIntExtra(PlaybackService.EXTRA_NOTIFICATION_CODE, -1);
if(code == -1 || type == -1) {
Log.d(TAG, "Bad arguments. Won't handle intent"); Log.d(TAG, "Bad arguments. Won't handle intent");
return; return;
} }
@ -310,6 +306,10 @@ public class PlaybackController {
onBufferUpdate(progress); onBufferUpdate(progress);
break; break;
case PlaybackService.NOTIFICATION_TYPE_RELOAD: case PlaybackService.NOTIFICATION_TYPE_RELOAD:
if (!isConnectedToPlaybackService()) {
bindToService();
return;
}
mediaInfoLoaded = false; mediaInfoLoaded = false;
queryService(); queryService();
onReloadNotification(intent.getIntExtra( onReloadNotification(intent.getIntExtra(