Merge pull request #2655 from ByteHamster/exo-player

Added ExoPlayer
This commit is contained in:
Martin Fietz 2018-07-22 18:22:16 +02:00 committed by GitHub
commit 9646cc883c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 324 additions and 103 deletions

View File

@ -52,7 +52,7 @@ public class VariableSpeedDialog {
builder.neutralText(R.string.close_label);
builder.onPositive((dialog, which) -> {
if (Build.VERSION.SDK_INT >= 16) { // just to be safe
UserPreferences.enableSonic(true);
UserPreferences.enableSonic();
if(showSpeedSelector) {
showSpeedSelectorDialog(context);
}

View File

@ -147,12 +147,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if(key.equals(UserPreferences.PREF_SONIC)) {
CheckBoxPreference prefSonic = (CheckBoxPreference) ui.findPreference(UserPreferences.PREF_SONIC);
if(prefSonic != null) {
prefSonic.setChecked(sharedPreferences.getBoolean(UserPreferences.PREF_SONIC, false));
}
}
}
@ -891,11 +886,10 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
}
private void checkSonicItemVisibility() {
if (Build.VERSION.SDK_INT >= 16) {
ui.findPreference(UserPreferences.PREF_SONIC).setEnabled(true);
} else {
Preference prefSonic = ui.findPreference(UserPreferences.PREF_SONIC);
prefSonic.setSummary("[Android 4.1+]\n" + prefSonic.getSummary());
if (Build.VERSION.SDK_INT < 16) {
ListPreference p = (ListPreference) ui.findPreference(UserPreferences.PREF_MEDIA_PLAYER);
p.setEntries(R.array.media_player_options_no_sonic);
p.setEntryValues(R.array.media_player_values_no_sonic);
}
}

View File

@ -109,12 +109,14 @@
</PreferenceCategory>
<PreferenceCategory android:title="@string/media_player">
<SwitchPreference
android:defaultValue="true"
android:enabled="false"
android:key="prefSonic"
android:summary="@string/pref_sonic_message"
android:title="@string/pref_sonic_title"/>
<ListPreference
android:defaultValue="sonic"
android:entries="@array/media_player_options"
android:key="prefMediaPlayer"
android:title="@string/media_player"
android:summary="@string/pref_media_player_message"
android:entryValues="@array/media_player_values"
app:useStockLayout="true"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/experimental_pref">

View File

@ -64,6 +64,9 @@ by The Apache Software Foundation, licensed under the Apache 2.0 license <a href
<h2>EventBus <a href="https://github.com/greenrobot/EventBus">(Link)</a></h2>
by greenrobot, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
<h2>ExoPlayer <a href="https://github.com/google/ExoPlayer">(Link)</a></h2>
by Google, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
<h2>flattr4j <a href="http://www.shredzone.org/projects/flattr4j/wiki">(Link)</a></h2>
licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>

View File

@ -62,6 +62,7 @@ project.ext {
rxJavaRulesVersion = "1.3.3.0"
triangleLabelViewVersion = "1.1.2"
exoPlayerVersion = "2.7.3"
audioPlayerVersion = "v1.0.17"
castCompanionLibVer = "2.9.1"

View File

@ -67,6 +67,7 @@ dependencies {
implementation "de.greenrobot:eventbus:$eventbusVersion"
implementation "io.reactivex:rxandroid:$rxAndroidVersion"
implementation "com.google.android.exoplayer:exoplayer:$exoPlayerVersion"
implementation "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion"
// Add casting features

View File

@ -65,7 +65,7 @@ class UpdateManager {
private static void onUpgrade(final int oldVersionCode, final int newVersionCode) {
if(oldVersionCode < 1050004) {
if(MediaPlayer.isPrestoLibraryInstalled(context) && Build.VERSION.SDK_INT >= 16) {
UserPreferences.enableSonic(true);
UserPreferences.enableSonic();
}
}
}

View File

@ -97,6 +97,7 @@ public class UserPreferences {
public static final String PREF_IMAGE_CACHE_SIZE = "prefImageCacheSize";
// Mediaplayer
public static final String PREF_MEDIA_PLAYER = "prefMediaPlayer";
private static final String PREF_PLAYBACK_SPEED = "prefPlaybackSpeed";
private static final String PREF_FAST_FORWARD_SECS = "prefFastForwardSecs";
private static final String PREF_REWIND_SECS = "prefRewindSecs";
@ -107,7 +108,6 @@ public class UserPreferences {
private static final String PREF_RIGHT_VOLUME = "prefRightVolume";
// Experimental
public static final String PREF_SONIC = "prefSonic";
private static final String PREF_STEREO_TO_MONO = "PrefStereoToMono";
public static final String PREF_CAST_ENABLED = "prefCast"; //Used for enabling Chromecast support
public static final int EPISODE_CLEANUP_QUEUE = -1;
@ -638,13 +638,15 @@ public class UserPreferences {
}
public static boolean useSonic() {
return prefs.getBoolean(PREF_SONIC, false);
return prefs.getString(PREF_MEDIA_PLAYER, "sonic").equals("sonic");
}
public static void enableSonic(boolean enable) {
prefs.edit()
.putBoolean(PREF_SONIC, enable)
.apply();
public static boolean useExoplayer() {
return prefs.getString(PREF_MEDIA_PLAYER, "sonic").equals("exoplayer");
}
public static void enableSonic() {
prefs.edit().putString(PREF_MEDIA_PLAYER, "sonic").apply();
}
public static boolean stereoToMono() {

View File

@ -0,0 +1,245 @@
package de.danoeh.antennapod.core.service.playback;
import android.content.Context;
import android.net.Uri;
import android.view.SurfaceHolder;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SeekParameters;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
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.DefaultDataSourceFactory;
import com.google.android.exoplayer2.util.Util;
import de.danoeh.antennapod.core.util.playback.IPlayer;
import org.antennapod.audio.MediaPlayer;
public class ExoPlayerWrapper implements IPlayer {
private final Context mContext;
private SimpleExoPlayer mExoPlayer;
private MediaSource mediaSource;
private MediaPlayer.OnSeekCompleteListener audioSeekCompleteListener;
private MediaPlayer.OnCompletionListener audioCompletionListener;
private MediaPlayer.OnErrorListener audioErrorListener;
ExoPlayerWrapper(Context context) {
mContext = context;
mExoPlayer = createPlayer();
}
private SimpleExoPlayer createPlayer() {
SimpleExoPlayer p = ExoPlayerFactory.newSimpleInstance(new DefaultRenderersFactory(mContext),
new DefaultTrackSelector(), new DefaultLoadControl());
p.setSeekParameters(SeekParameters.PREVIOUS_SYNC);
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
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
if (playbackState == Player.STATE_ENDED) {
audioCompletionListener.onCompletion(null);
}
}
@Override
public void onRepeatModeChanged(int repeatMode) {
}
@Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
}
@Override
public void onPlayerError(ExoPlaybackException error) {
audioErrorListener.onError(null, 0, 0);
}
@Override
public void onPositionDiscontinuity(int reason) {
}
@Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
}
@Override
public void onSeekProcessed() {
audioSeekCompleteListener.onSeekComplete(null);
}
});
return p;
}
@Override
public boolean canSetSpeed() {
return true;
}
@Override
public boolean canDownmix() {
return false;
}
@Override
public int getCurrentPosition() {
return (int) mExoPlayer.getCurrentPosition();
}
@Override
public float getCurrentSpeedMultiplier() {
return mExoPlayer.getPlaybackParameters().speed;
}
@Override
public int getDuration() {
if (mExoPlayer.getDuration() == C.TIME_UNSET) {
return PlaybackServiceMediaPlayer.INVALID_TIME;
}
return (int) mExoPlayer.getDuration();
}
@Override
public boolean isPlaying() {
return mExoPlayer.getPlayWhenReady();
}
@Override
public void pause() {
mExoPlayer.setPlayWhenReady(false);
}
@Override
public void prepare() throws IllegalStateException {
mExoPlayer.prepare(mediaSource);
}
@Override
public void release() {
if (mExoPlayer != null) {
mExoPlayer.release();
}
audioSeekCompleteListener = null;
audioCompletionListener = null;
audioErrorListener = null;
}
@Override
public void reset() {
mExoPlayer.release();
mExoPlayer = createPlayer();
}
@Override
public void seekTo(int i) throws IllegalStateException {
mExoPlayer.seekTo(i);
}
@Override
public void setAudioStreamType(int i) {
AudioAttributes a = mExoPlayer.getAudioAttributes();
AudioAttributes.Builder b = new AudioAttributes.Builder();
b.setContentType(i);
b.setFlags(a.flags);
b.setUsage(a.usage);
mExoPlayer.setAudioAttributes(b.build());
}
@Override
public void setDataSource(String s) throws IllegalArgumentException, IllegalStateException {
DataSource.Factory dataSourceFactory =
new DefaultDataSourceFactory(mContext, Util.getUserAgent(mContext, mContext.getPackageName()), null);
ExtractorMediaSource.Factory f = new ExtractorMediaSource.Factory(dataSourceFactory);
mediaSource = f.createMediaSource(Uri.parse(s));
}
@Override
public void setDisplay(SurfaceHolder sh) {
mExoPlayer.setVideoSurfaceHolder(sh);
}
@Override
public void setPlaybackSpeed(float v) {
PlaybackParameters params = mExoPlayer.getPlaybackParameters();
mExoPlayer.setPlaybackParameters(new PlaybackParameters(v, params.pitch));
}
@Override
public void setDownmix(boolean b) {
}
@Override
public void setVolume(float v, float v1) {
mExoPlayer.setVolume(v);
}
@Override
public void setWakeMode(Context context, int i) {
}
@Override
public void start() {
mExoPlayer.setPlayWhenReady(true);
}
@Override
public void stop() {
mExoPlayer.stop();
}
void setOnCompletionListener(MediaPlayer.OnCompletionListener audioCompletionListener) {
this.audioCompletionListener = audioCompletionListener;
}
void setOnSeekCompleteListener(MediaPlayer.OnSeekCompleteListener audioSeekCompleteListener) {
this.audioSeekCompleteListener = audioSeekCompleteListener;
}
void setOnErrorListener(MediaPlayer.OnErrorListener audioErrorListener) {
this.audioErrorListener = audioErrorListener;
}
int getVideoWidth() {
if (mExoPlayer.getVideoFormat() == null) {
return 0;
}
return mExoPlayer.getVideoFormat().width;
}
int getVideoHeight() {
if (mExoPlayer.getVideoFormat() == null) {
return 0;
}
return mExoPlayer.getVideoFormat().height;
}
}

View File

@ -313,7 +313,10 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
Log.d(TAG, "Resource prepared");
if (mediaType == MediaType.VIDEO) {
if (mediaType == MediaType.VIDEO && mediaPlayer instanceof ExoPlayerWrapper) {
ExoPlayerWrapper vp = (ExoPlayerWrapper) mediaPlayer;
videoSize = new Pair<>(vp.getVideoWidth(), vp.getVideoHeight());
} else if(mediaType == MediaType.VIDEO && mediaPlayer instanceof VideoPlayer) {
VideoPlayer vp = (VideoPlayer) mediaPlayer;
videoSize = new Pair<>(vp.getVideoWidth(), vp.getVideoHeight());
}
@ -447,7 +450,8 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|| playerStatus == PlayerStatus.PAUSED
|| playerStatus == PlayerStatus.PREPARED) {
retVal = mediaPlayer.getDuration();
} else if (media != null && media.getDuration() > 0) {
}
if (retVal <= 0 && media != null && media.getDuration() > 0) {
retVal = media.getDuration();
}
@ -627,6 +631,9 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
} else if (mediaPlayer instanceof AudioPlayer) {
AudioPlayer ap = (AudioPlayer) mediaPlayer;
ap.setOnErrorListener((mediaPlayer, i, i1) -> true);
} else if (mediaPlayer instanceof ExoPlayerWrapper) {
ExoPlayerWrapper ap = (ExoPlayerWrapper) mediaPlayer;
ap.setOnErrorListener((mediaPlayer, i, i1) -> true);
}
}
@ -682,6 +689,10 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
Pair<Integer, Integer> res;
if (mediaPlayer == null || playerStatus == PlayerStatus.ERROR || mediaType != MediaType.VIDEO) {
res = null;
} else if (mediaPlayer instanceof ExoPlayerWrapper) {
ExoPlayerWrapper vp = (ExoPlayerWrapper) mediaPlayer;
videoSize = new Pair<>(vp.getVideoWidth(), vp.getVideoHeight());
res = videoSize;
} else {
VideoPlayer vp = (VideoPlayer) mediaPlayer;
videoSize = new Pair<>(vp.getVideoWidth(), vp.getVideoHeight());
@ -715,11 +726,15 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
mediaPlayer = null;
return;
}
if (media.getMediaType() == MediaType.VIDEO) {
if (UserPreferences.useExoplayer()) {
mediaPlayer = new ExoPlayerWrapper(context);
} else if (media.getMediaType() == MediaType.VIDEO) {
mediaPlayer = new VideoPlayer();
} else {
mediaPlayer = new AudioPlayer(context);
}
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
setMediaPlayerListeners(mediaPlayer);
@ -896,6 +911,11 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
ap.setOnBufferingUpdateListener(audioBufferingUpdateListener);
ap.setOnInfoListener(audioInfoListener);
ap.setOnSpeedAdjustmentAvailableChangedListener(audioSetSpeedAbilityListener);
} else if (mp instanceof ExoPlayerWrapper) {
ExoPlayerWrapper ap = (ExoPlayerWrapper) mp;
ap.setOnCompletionListener(audioCompletionListener);
ap.setOnSeekCompleteListener(audioSeekCompleteListener);
ap.setOnErrorListener(audioErrorListener);
} else {
Log.w(TAG, "Unknown media player: " + mp);
}

View File

@ -21,17 +21,11 @@ public class AudioPlayer extends MediaPlayer implements IPlayer {
private final SharedPreferences.OnSharedPreferenceChangeListener sonicListener =
(sharedPreferences, key) -> {
if (key.equals(UserPreferences.PREF_SONIC)) {
if (key.equals(UserPreferences.PREF_MEDIA_PLAYER)) {
checkMpi();
}
};
@Override
public void setScreenOnWhilePlaying(boolean screenOn) {
Log.e(TAG, "Setting screen on while playing not supported in Audio Player");
throw new UnsupportedOperationException("Setting screen on while playing not supported in Audio Player");
}
@Override
public void setDisplay(SurfaceHolder sh) {
if (sh != null) {
@ -40,11 +34,6 @@ public class AudioPlayer extends MediaPlayer implements IPlayer {
}
}
@Override
public void setVideoScalingMode(int mode) {
throw new UnsupportedOperationException("Setting scaling mode is not supported in Audio Player");
}
@Override
protected boolean useSonic() {
return UserPreferences.useSonic();

View File

@ -6,13 +6,11 @@ import android.view.SurfaceHolder;
import java.io.IOException;
public interface IPlayer {
boolean canSetPitch();
boolean canSetSpeed();
boolean canDownmix();
float getCurrentPitchStepsAdjustment();
int getCurrentPosition();
@ -20,20 +18,12 @@ public interface IPlayer {
int getDuration();
float getMaxSpeedMultiplier();
float getMinSpeedMultiplier();
boolean isLooping();
boolean isPlaying();
void pause();
void prepare() throws IllegalStateException, IOException;
void prepareAsync();
void release();
void reset();
@ -42,21 +32,11 @@ public interface IPlayer {
void setAudioStreamType(int streamtype);
void setScreenOnWhilePlaying(boolean screenOn);
void setDataSource(String path) throws IllegalStateException, IOException,
IllegalArgumentException, SecurityException;
void setDisplay(SurfaceHolder sh);
void setEnableSpeedAdjustment(boolean enableSpeedAdjustment);
void setLooping(boolean looping);
void setPitchStepsAdjustment(float pitchSteps);
void setPlaybackPitch(float f);
void setPlaybackSpeed(float f);
void setDownmix(boolean enable);
@ -67,7 +47,5 @@ public interface IPlayer {
void stop();
void setVideoScalingMode(int mode);
void setWakeMode(Context context, int mode);
}

View File

@ -6,11 +6,6 @@ import android.util.Log;
public class VideoPlayer extends MediaPlayer implements IPlayer {
private static final String TAG = "VideoPlayer";
@Override
public boolean canSetPitch() {
return false;
}
@Override
public boolean canSetSpeed() {
return false;
@ -21,44 +16,11 @@ public class VideoPlayer extends MediaPlayer implements IPlayer {
return false;
}
@Override
public float getCurrentPitchStepsAdjustment() {
return 1;
}
@Override
public float getCurrentSpeedMultiplier() {
return 1;
}
@Override
public float getMaxSpeedMultiplier() {
return 1;
}
@Override
public float getMinSpeedMultiplier() {
return 1;
}
@Override
public void setEnableSpeedAdjustment(boolean enableSpeedAdjustment) throws UnsupportedOperationException {
Log.e(TAG, "Setting enable speed adjustment unsupported in video player");
throw new UnsupportedOperationException("Setting enable speed adjustment unsupported in video player");
}
@Override
public void setPitchStepsAdjustment(float pitchSteps) {
Log.e(TAG, "Setting pitch steps adjustment unsupported in video player");
throw new UnsupportedOperationException("Setting pitch steps adjustment unsupported in video player");
}
@Override
public void setPlaybackPitch(float f) {
Log.e(TAG, "Setting playback pitch unsupported in video player");
throw new UnsupportedOperationException("Setting playback pitch unsupported in video player");
}
@Override
public void setPlaybackSpeed(float f) {
Log.e(TAG, "Setting playback speed unsupported in video player");

View File

@ -183,6 +183,28 @@
<item>3</item>
</string-array>
<string-array name="media_player_options">
<item>@string/media_player_builtin</item>
<item>@string/media_player_sonic</item>
<item>@string/media_player_exoplayer</item>
</string-array>
<string-array name="media_player_values">
<item>builtin</item>
<item>sonic</item>
<item>exoplayer</item>
</string-array>
<string-array name="media_player_options_no_sonic">
<item>@string/media_player_builtin</item>
<item>@string/media_player_exoplayer</item>
</string-array>
<string-array name="media_player_values_no_sonic">
<item>builtin</item>
<item>exoplayer</item>
</string-array>
<string-array name="episode_filter_options">
<item>@string/hide_unplayed_episodes_label</item>
<item>@string/hide_paused_episodes_label</item>

View File

@ -446,8 +446,7 @@
<string name="crash_report_sum">Send the latest crash report via e-mail</string>
<string name="send_email">Send e-mail</string>
<string name="experimental_pref">Experimental</string>
<string name="pref_sonic_title">Sonic Media Player</string>
<string name="pref_sonic_message">Use built-in sonic media player as a replacement for Android\'s native mediaplayer and Prestissimo</string>
<string name="pref_media_player_message">Select which media player to use to play files</string>
<string name="pref_current_value">Current value: %1$s</string>
<string name="pref_proxy_title">Proxy</string>
<string name="pref_proxy_sum">Set a network proxy</string>
@ -459,6 +458,9 @@
<string name="pref_cast_message_free_flavor">Chromecast requires third party proprietary libraries that are disabled in this version of AntennaPod</string>
<string name="pref_enqueue_downloaded_title">Enqueue Downloaded</string>
<string name="pref_enqueue_downloaded_summary">Add downloaded episodes to the queue</string>
<string name="media_player_builtin">Built-in Android player</string>
<string name="media_player_sonic" translatable="false">Sonic Media Player</string>
<string name="media_player_exoplayer" translatable="false">Exoplayer</string>
<string name="pref_videoBehavior_title">Video behavior</string>
<string name="pref_videoBehavior_sum">Behavior when leaving video playback</string>
<string name="stop_playback">Stop playback</string>