Remove deprecated media players (#6354)
This commit is contained in:
parent
3e077e5653
commit
ccea00e405
|
@ -125,7 +125,6 @@ dependencies {
|
|||
implementation "com.joanzapata.iconify:android-iconify-fontawesome:$iconifyVersion"
|
||||
implementation "com.joanzapata.iconify:android-iconify-material:$iconifyVersion"
|
||||
implementation 'com.leinardi.android:speed-dial:3.2.0'
|
||||
implementation "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion"
|
||||
implementation 'com.github.ByteHamster:SearchPreference:v2.0.0'
|
||||
implementation 'com.github.skydoves:balloon:1.4.0'
|
||||
implementation 'com.github.xabaras:RecyclerViewSwipeDecorator:1.3'
|
||||
|
|
|
@ -4,45 +4,38 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.view.KeyEvent;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.rule.ActivityTestRule;
|
||||
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
|
||||
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.util.LongList;
|
||||
import de.danoeh.antennapod.core.util.playback.PlaybackController;
|
||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
import de.danoeh.antennapod.model.feed.FeedItemFilter;
|
||||
import de.danoeh.antennapod.model.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.model.feed.SortOrder;
|
||||
import de.danoeh.antennapod.playback.base.PlayerStatus;
|
||||
import de.danoeh.antennapod.storage.preferences.UserPreferences;
|
||||
import de.test.antennapod.EspressoTestUtils;
|
||||
import de.test.antennapod.IgnoreOnCi;
|
||||
import de.test.antennapod.ui.UITestUtils;
|
||||
import org.awaitility.Awaitility;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
import de.danoeh.antennapod.model.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
|
||||
import de.danoeh.antennapod.storage.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.util.LongList;
|
||||
import de.danoeh.antennapod.core.util.playback.PlaybackController;
|
||||
import de.test.antennapod.EspressoTestUtils;
|
||||
import de.test.antennapod.IgnoreOnCi;
|
||||
import de.test.antennapod.ui.UITestUtils;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
|
||||
|
@ -69,31 +62,20 @@ import static org.junit.Assert.assertTrue;
|
|||
*/
|
||||
@LargeTest
|
||||
@IgnoreOnCi
|
||||
@RunWith(Parameterized.class)
|
||||
public class PlaybackTest {
|
||||
@Rule
|
||||
public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity.class, false, false);
|
||||
|
||||
@Parameterized.Parameter(value = 0)
|
||||
public String playerToUse;
|
||||
private UITestUtils uiTestUtils;
|
||||
protected Context context;
|
||||
private PlaybackController controller;
|
||||
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
public static Collection<Object[]> initParameters() {
|
||||
return Arrays.asList(new Object[][] { { "exoplayer" }, { "builtin" }, { "sonic" } });
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
context = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
EspressoTestUtils.clearPreferences();
|
||||
EspressoTestUtils.clearDatabase();
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
prefs.edit().putString(UserPreferences.PREF_MEDIA_PLAYER, playerToUse).apply();
|
||||
|
||||
uiTestUtils = new UITestUtils(context);
|
||||
uiTestUtils.setup();
|
||||
}
|
||||
|
|
|
@ -6,11 +6,9 @@ import android.text.SpannableString;
|
|||
import android.text.style.ForegroundColorSpan;
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.event.PlayerErrorEvent;
|
||||
import de.danoeh.antennapod.storage.preferences.UserPreferences;
|
||||
|
||||
public class MediaPlayerErrorDialog {
|
||||
public static void show(Activity activity, PlayerErrorEvent event) {
|
||||
|
@ -25,13 +23,6 @@ public class MediaPlayerErrorDialog {
|
|||
errorDialog.setMessage(errorMessage);
|
||||
errorDialog.setPositiveButton(android.R.string.ok, (dialog, which) ->
|
||||
((MainActivity) activity).getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED));
|
||||
if (!UserPreferences.useExoplayer()) {
|
||||
errorDialog.setNeutralButton(R.string.media_player_switch_to_exoplayer, (dialog, which) -> {
|
||||
UserPreferences.enableExoplayer();
|
||||
((MainActivity) activity).showSnackbarAbovePlayer(
|
||||
R.string.media_player_switched_to_exoplayer, Snackbar.LENGTH_LONG);
|
||||
});
|
||||
}
|
||||
errorDialog.create().show();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,31 +63,12 @@ public class PlaybackControlsDialog extends DialogFragment {
|
|||
}
|
||||
|
||||
private void setupUi() {
|
||||
final CheckBox stereoToMono = dialog.findViewById(R.id.stereo_to_mono);
|
||||
stereoToMono.setChecked(UserPreferences.stereoToMono());
|
||||
if (controller != null && !controller.canDownmix()) {
|
||||
stereoToMono.setEnabled(false);
|
||||
String sonicOnly = getString(R.string.sonic_only);
|
||||
stereoToMono.setText(getString(R.string.stereo_to_mono) + " [" + sonicOnly + "]");
|
||||
}
|
||||
|
||||
final CheckBox skipSilence = dialog.findViewById(R.id.skipSilence);
|
||||
skipSilence.setChecked(UserPreferences.isSkipSilence());
|
||||
if (!UserPreferences.useExoplayer()) {
|
||||
skipSilence.setEnabled(false);
|
||||
String exoplayerOnly = getString(R.string.exoplayer_only);
|
||||
skipSilence.setText(getString(R.string.pref_skip_silence_title) + " [" + exoplayerOnly + "]");
|
||||
}
|
||||
skipSilence.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
UserPreferences.setSkipSilence(isChecked);
|
||||
controller.setSkipSilence(isChecked);
|
||||
});
|
||||
stereoToMono.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
UserPreferences.stereoToMono(isChecked);
|
||||
if (controller != null) {
|
||||
controller.setDownmix(isChecked);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupAudioTracks() {
|
||||
|
|
|
@ -57,9 +57,6 @@ public class PreferenceUpgrader {
|
|||
}
|
||||
}
|
||||
if (oldVersion < 1070300) {
|
||||
prefs.edit().putString(UserPreferences.PREF_MEDIA_PLAYER,
|
||||
UserPreferences.PREF_MEDIA_PLAYER_EXOPLAYER).apply();
|
||||
|
||||
if (prefs.getBoolean("prefEnableAutoDownloadOnMobile", false)) {
|
||||
UserPreferences.setAllowMobileAutoDownload(true);
|
||||
}
|
||||
|
|
|
@ -32,12 +32,6 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:text="@string/pref_skip_silence_title" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/stereo_to_mono"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/stereo_to_mono" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
android:id="@+id/imgvCover"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:contentDescription="@string/media_player"
|
||||
android:adjustViewBounds="true"
|
||||
android:cropToPadding="true"
|
||||
android:maxWidth="96dp"
|
||||
|
|
|
@ -111,14 +111,4 @@
|
|||
android:summary="@string/pref_skip_keeps_episodes_sum"
|
||||
android:title="@string/pref_skip_keeps_episodes_title"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/experimental_pref">
|
||||
<de.danoeh.antennapod.preferences.MaterialListPreference
|
||||
android:defaultValue="exoplayer"
|
||||
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"/>
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
|
|
|
@ -45,7 +45,6 @@ project.ext {
|
|||
rxJavaVersion = "2.2.2"
|
||||
iconifyVersion = "2.2.2"
|
||||
exoPlayerVersion = "2.14.2"
|
||||
audioPlayerVersion = "v2.0.0"
|
||||
|
||||
// Google Play build
|
||||
wearableSupportVersion = "2.6.0"
|
||||
|
|
|
@ -66,7 +66,6 @@ dependencies {
|
|||
implementation "com.google.android.exoplayer:exoplayer-core:$exoPlayerVersion"
|
||||
implementation "com.google.android.exoplayer:exoplayer-ui:$exoPlayerVersion"
|
||||
implementation "com.google.android.exoplayer:extension-okhttp:$exoPlayerVersion"
|
||||
implementation "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion"
|
||||
|
||||
// Non-free dependencies:
|
||||
playApi "com.google.android.support:wearable:$wearableSupportVersion"
|
||||
|
|
|
@ -40,12 +40,10 @@ import de.danoeh.antennapod.storage.preferences.UserPreferences;
|
|||
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
|
||||
import de.danoeh.antennapod.core.service.download.HttpCredentialEncoder;
|
||||
import de.danoeh.antennapod.core.util.NetworkUtils;
|
||||
import de.danoeh.antennapod.core.util.playback.IPlayer;
|
||||
import de.danoeh.antennapod.model.playback.Playable;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import org.antennapod.audio.MediaPlayer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -53,19 +51,21 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ExoPlayerWrapper implements IPlayer {
|
||||
public class ExoPlayerWrapper {
|
||||
public static final int BUFFERING_STARTED = -1;
|
||||
public static final int BUFFERING_ENDED = -2;
|
||||
private static final String TAG = "ExoPlayerWrapper";
|
||||
public static final int ERROR_CODE_OFFSET = 1000;
|
||||
|
||||
private final Context context;
|
||||
private final Disposable bufferingUpdateDisposable;
|
||||
private SimpleExoPlayer exoPlayer;
|
||||
private MediaSource mediaSource;
|
||||
private MediaPlayer.OnSeekCompleteListener audioSeekCompleteListener;
|
||||
private MediaPlayer.OnCompletionListener audioCompletionListener;
|
||||
private Runnable audioSeekCompleteListener;
|
||||
private Runnable audioCompletionListener;
|
||||
private Consumer<String> audioErrorListener;
|
||||
private MediaPlayer.OnBufferingUpdateListener bufferingUpdateListener;
|
||||
private Consumer<Integer> bufferingUpdateListener;
|
||||
private PlaybackParameters playbackParameters;
|
||||
private MediaPlayer.OnInfoListener infoListener;
|
||||
private DefaultTrackSelector trackSelector;
|
||||
|
||||
ExoPlayerWrapper(Context context) {
|
||||
|
@ -76,7 +76,7 @@ public class ExoPlayerWrapper implements IPlayer {
|
|||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(tickNumber -> {
|
||||
if (bufferingUpdateListener != null) {
|
||||
bufferingUpdateListener.onBufferingUpdate(null, exoPlayer.getBufferedPercentage());
|
||||
bufferingUpdateListener.accept(exoPlayer.getBufferedPercentage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -97,11 +97,11 @@ public class ExoPlayerWrapper implements IPlayer {
|
|||
@Override
|
||||
public void onPlaybackStateChanged(@Player.State int playbackState) {
|
||||
if (audioCompletionListener != null && playbackState == Player.STATE_ENDED) {
|
||||
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);
|
||||
audioCompletionListener.run();
|
||||
} else if (bufferingUpdateListener != null && playbackState == Player.STATE_BUFFERING) {
|
||||
bufferingUpdateListener.accept(BUFFERING_STARTED);
|
||||
} else if (bufferingUpdateListener != null) {
|
||||
bufferingUpdateListener.accept(BUFFERING_ENDED);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,28 +130,20 @@ public class ExoPlayerWrapper implements IPlayer {
|
|||
@NonNull Player.PositionInfo newPosition,
|
||||
@Player.DiscontinuityReason int reason) {
|
||||
if (audioSeekCompleteListener != null && reason == Player.DISCONTINUITY_REASON_SEEK) {
|
||||
audioSeekCompleteListener.onSeekComplete(null);
|
||||
audioSeekCompleteListener.run();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDownmix() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentPosition() {
|
||||
return (int) exoPlayer.getCurrentPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getCurrentSpeedMultiplier() {
|
||||
return playbackParameters.speed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDuration() {
|
||||
if (exoPlayer.getDuration() == C.TIME_UNSET) {
|
||||
return Playable.INVALID_TIME;
|
||||
|
@ -159,23 +151,19 @@ public class ExoPlayerWrapper implements IPlayer {
|
|||
return (int) exoPlayer.getDuration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlaying() {
|
||||
return exoPlayer.getPlayWhenReady();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause() {
|
||||
exoPlayer.pause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare() throws IllegalStateException {
|
||||
exoPlayer.setMediaSource(mediaSource, false);
|
||||
exoPlayer.prepare();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
bufferingUpdateDisposable.dispose();
|
||||
if (exoPlayer != null) {
|
||||
|
@ -187,21 +175,18 @@ public class ExoPlayerWrapper implements IPlayer {
|
|||
bufferingUpdateListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
exoPlayer.release();
|
||||
createPlayer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekTo(int i) throws IllegalStateException {
|
||||
exoPlayer.seekTo(i);
|
||||
if (audioSeekCompleteListener != null) {
|
||||
audioSeekCompleteListener.onSeekComplete(null);
|
||||
audioSeekCompleteListener.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAudioStreamType(int i) {
|
||||
AudioAttributes a = exoPlayer.getAudioAttributes();
|
||||
AudioAttributes.Builder b = new AudioAttributes.Builder();
|
||||
|
@ -235,51 +220,34 @@ public class ExoPlayerWrapper implements IPlayer {
|
|||
mediaSource = f.createMediaSource(mediaItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataSource(String s) throws IllegalArgumentException, IllegalStateException {
|
||||
setDataSource(s, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplay(SurfaceHolder sh) {
|
||||
exoPlayer.setVideoSurfaceHolder(sh);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlaybackParams(float speed, boolean skipSilence) {
|
||||
playbackParameters = new PlaybackParameters(speed, playbackParameters.pitch);
|
||||
exoPlayer.setSkipSilenceEnabled(skipSilence);
|
||||
exoPlayer.setPlaybackParameters(playbackParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDownmix(boolean b) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVolume(float v, float v1) {
|
||||
exoPlayer.setVolume(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWakeMode(Context context, int i) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
exoPlayer.play();
|
||||
// Can't set params when paused - so always set it on start in case they changed
|
||||
exoPlayer.setPlaybackParameters(playbackParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
exoPlayer.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAudioTracks() {
|
||||
List<String> trackNames = new ArrayList<>();
|
||||
TrackNameProvider trackNameProvider = new DefaultTrackNameProvider(context.getResources());
|
||||
|
@ -302,7 +270,6 @@ public class ExoPlayerWrapper implements IPlayer {
|
|||
return formats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAudioTrack(int track) {
|
||||
MappingTrackSelector.MappedTrackInfo trackInfo = trackSelector.getCurrentMappedTrackInfo();
|
||||
if (trackInfo == null) {
|
||||
|
@ -324,7 +291,6 @@ public class ExoPlayerWrapper implements IPlayer {
|
|||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSelectedAudioTrack() {
|
||||
TrackSelectionArray trackSelections = exoPlayer.getCurrentTrackSelections();
|
||||
List<Format> availableFormats = getFormats();
|
||||
|
@ -340,11 +306,11 @@ public class ExoPlayerWrapper implements IPlayer {
|
|||
return -1;
|
||||
}
|
||||
|
||||
void setOnCompletionListener(MediaPlayer.OnCompletionListener audioCompletionListener) {
|
||||
void setOnCompletionListener(Runnable audioCompletionListener) {
|
||||
this.audioCompletionListener = audioCompletionListener;
|
||||
}
|
||||
|
||||
void setOnSeekCompleteListener(MediaPlayer.OnSeekCompleteListener audioSeekCompleteListener) {
|
||||
void setOnSeekCompleteListener(Runnable audioSeekCompleteListener) {
|
||||
this.audioSeekCompleteListener = audioSeekCompleteListener;
|
||||
}
|
||||
|
||||
|
@ -366,11 +332,7 @@ public class ExoPlayerWrapper implements IPlayer {
|
|||
return exoPlayer.getVideoFormat().height;
|
||||
}
|
||||
|
||||
void setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener bufferingUpdateListener) {
|
||||
void setOnBufferingUpdateListener(Consumer<Integer> bufferingUpdateListener) {
|
||||
this.bufferingUpdateListener = bufferingUpdateListener;
|
||||
}
|
||||
|
||||
public void setOnInfoListener(MediaPlayer.OnInfoListener infoListener) {
|
||||
this.infoListener = infoListener;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,47 +6,34 @@ import android.content.res.Configuration;
|
|||
import android.media.AudioManager;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.PowerManager;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.SurfaceHolder;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.media.AudioAttributesCompat;
|
||||
import androidx.media.AudioFocusRequestCompat;
|
||||
import androidx.media.AudioManagerCompat;
|
||||
import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
|
||||
import de.danoeh.antennapod.event.PlayerErrorEvent;
|
||||
import de.danoeh.antennapod.event.playback.BufferUpdateEvent;
|
||||
import de.danoeh.antennapod.event.playback.SpeedChangedEvent;
|
||||
import de.danoeh.antennapod.core.util.playback.MediaPlayerError;
|
||||
import de.danoeh.antennapod.model.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.model.feed.FeedPreferences;
|
||||
import de.danoeh.antennapod.model.feed.VolumeAdaptionSetting;
|
||||
import de.danoeh.antennapod.model.playback.MediaType;
|
||||
import de.danoeh.antennapod.model.playback.Playable;
|
||||
import de.danoeh.antennapod.playback.base.PlaybackServiceMediaPlayer;
|
||||
import de.danoeh.antennapod.playback.base.PlayerStatus;
|
||||
import org.antennapod.audio.MediaPlayer;
|
||||
import de.danoeh.antennapod.playback.base.RewindAfterPauseUtils;
|
||||
import de.danoeh.antennapod.storage.preferences.UserPreferences;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import de.danoeh.antennapod.model.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.model.feed.FeedPreferences;
|
||||
import de.danoeh.antennapod.model.playback.MediaType;
|
||||
import de.danoeh.antennapod.model.feed.VolumeAdaptionSetting;
|
||||
import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
|
||||
import de.danoeh.antennapod.storage.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.playback.base.RewindAfterPauseUtils;
|
||||
import de.danoeh.antennapod.core.util.playback.AudioPlayer;
|
||||
import de.danoeh.antennapod.core.util.playback.IPlayer;
|
||||
import de.danoeh.antennapod.model.playback.Playable;
|
||||
import de.danoeh.antennapod.core.util.playback.VideoPlayer;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
* Manages the MediaPlayer object of the PlaybackService.
|
||||
|
@ -57,7 +44,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
private final AudioManager audioManager;
|
||||
|
||||
private volatile PlayerStatus statusBeforeSeeking;
|
||||
private volatile IPlayer mediaPlayer;
|
||||
private volatile ExoPlayerWrapper mediaPlayer;
|
||||
private volatile Playable media;
|
||||
|
||||
private volatile boolean stream;
|
||||
|
@ -67,100 +54,15 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
private volatile Pair<Integer, Integer> videoSize;
|
||||
private final AudioFocusRequestCompat audioFocusRequest;
|
||||
private final Handler audioFocusCanceller;
|
||||
|
||||
/**
|
||||
* Some asynchronous calls might change the state of the MediaPlayer object. Therefore calls in other threads
|
||||
* have to wait until these operations have finished.
|
||||
*/
|
||||
private final PlayerLock playerLock;
|
||||
private final PlayerExecutor executor;
|
||||
private boolean useCallerThread = true;
|
||||
private boolean isShutDown = false;
|
||||
|
||||
|
||||
private CountDownLatch seekLatch;
|
||||
|
||||
/**
|
||||
* All ExoPlayer methods must be executed on the same thread.
|
||||
* We use the main application thread. This class allows to
|
||||
* "fake" an executor that just calls the methods on the
|
||||
* calling thread instead of submitting to an executor.
|
||||
* Other players are still executed in a background thread.
|
||||
*/
|
||||
private class PlayerExecutor {
|
||||
private ThreadPoolExecutor threadPool;
|
||||
|
||||
public Future<?> submit(Runnable r) {
|
||||
if (useCallerThread) {
|
||||
r.run();
|
||||
return new FutureTask<Void>(() -> {}, null);
|
||||
} else {
|
||||
return threadPool.submit(r);
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
threadPool.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All ExoPlayer methods must be executed on the same thread.
|
||||
* We use the main application thread. This class allows to
|
||||
* "fake" a lock that does nothing. A lock is not needed if
|
||||
* everything is called on the same thread.
|
||||
* Other players are still executed in a background thread and
|
||||
* therefore use a real lock.
|
||||
*/
|
||||
private class PlayerLock {
|
||||
private ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
public void lock() {
|
||||
if (!useCallerThread) {
|
||||
lock.lock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean tryLock(int i, TimeUnit milliseconds) throws InterruptedException {
|
||||
if (!useCallerThread) {
|
||||
return lock.tryLock(i, milliseconds);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean tryLock() {
|
||||
if (!useCallerThread) {
|
||||
return lock.tryLock();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void unlock() {
|
||||
if (!useCallerThread) {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isHeldByCurrentThread() {
|
||||
if (!useCallerThread) {
|
||||
return lock.isHeldByCurrentThread();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public LocalPSMP(@NonNull Context context,
|
||||
@NonNull PlaybackServiceMediaPlayer.PSMPCallback callback) {
|
||||
super(context, callback);
|
||||
this.audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
this.playerLock = new PlayerLock();
|
||||
this.startWhenPrepared = new AtomicBoolean(false);
|
||||
audioFocusCanceller = new Handler(Looper.getMainLooper());
|
||||
|
||||
executor = new PlayerExecutor();
|
||||
executor.threadPool = new ThreadPoolExecutor(1, 1, 5, TimeUnit.MINUTES, new LinkedBlockingDeque<>(),
|
||||
(r, executor) -> Log.d(TAG, "Rejected execution of runnable"));
|
||||
|
||||
mediaPlayer = null;
|
||||
statusBeforeSeeking = null;
|
||||
pausedBecauseOfTransientAudiofocusLoss = false;
|
||||
|
@ -207,18 +109,12 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
@Override
|
||||
public void playMediaObject(@NonNull final Playable playable, final boolean stream, final boolean startWhenPrepared, final boolean prepareImmediately) {
|
||||
Log.d(TAG, "playMediaObject(...)");
|
||||
useCallerThread = UserPreferences.useExoplayer();
|
||||
executor.submit(() -> {
|
||||
playerLock.lock();
|
||||
try {
|
||||
playMediaObject(playable, false, stream, startWhenPrepared, prepareImmediately);
|
||||
} catch (RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
} finally {
|
||||
playerLock.unlock();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -230,11 +126,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
* @see #playMediaObject(Playable, boolean, boolean, boolean)
|
||||
*/
|
||||
private void playMediaObject(@NonNull final Playable playable, final boolean forceReset, final boolean stream, final boolean startWhenPrepared, final boolean prepareImmediately) {
|
||||
if (!playerLock.isHeldByCurrentThread()) {
|
||||
throw new IllegalStateException("method requires playerLock");
|
||||
}
|
||||
|
||||
|
||||
if (media != null) {
|
||||
if (!forceReset && media.getIdentifier().equals(playable.getIdentifier())
|
||||
&& playerStatus == PlayerStatus.PLAYING) {
|
||||
|
@ -253,7 +144,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
|
||||
if (!media.getIdentifier().equals(playable.getIdentifier())) {
|
||||
final Playable oldMedia = media;
|
||||
executor.submit(() -> callback.onPostPlayback(oldMedia, false, false, true));
|
||||
callback.onPostPlayback(oldMedia, false, false, true);
|
||||
}
|
||||
|
||||
setPlayerStatus(PlayerStatus.INDETERMINATE, null);
|
||||
|
@ -313,14 +204,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
*/
|
||||
@Override
|
||||
public void resume() {
|
||||
executor.submit(() -> {
|
||||
playerLock.lock();
|
||||
resumeSync();
|
||||
playerLock.unlock();
|
||||
});
|
||||
}
|
||||
|
||||
private void resumeSync() {
|
||||
if (playerStatus == PlayerStatus.PAUSED || playerStatus == PlayerStatus.PREPARED) {
|
||||
int focusGained = AudioManagerCompat.requestAudioFocus(audioManager, audioFocusRequest);
|
||||
|
||||
|
@ -336,7 +219,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
int newPosition = RewindAfterPauseUtils.calculatePositionWithRewind(
|
||||
media.getPosition(),
|
||||
media.getLastPlayedTime());
|
||||
seekToSync(newPosition);
|
||||
seekTo(newPosition);
|
||||
}
|
||||
mediaPlayer.start();
|
||||
|
||||
|
@ -363,8 +246,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
*/
|
||||
@Override
|
||||
public void pause(final boolean abandonFocus, final boolean reinit) {
|
||||
executor.submit(() -> {
|
||||
playerLock.lock();
|
||||
releaseWifiLockIfNecessary();
|
||||
if (playerStatus == PlayerStatus.PLAYING) {
|
||||
Log.d(TAG, "Pausing playback.");
|
||||
|
@ -381,9 +262,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
} else {
|
||||
Log.d(TAG, "Ignoring call to pause: Player is in " + playerStatus + " state");
|
||||
}
|
||||
|
||||
playerLock.unlock();
|
||||
});
|
||||
}
|
||||
|
||||
private void abandonAudioFocus() {
|
||||
|
@ -398,50 +276,30 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
*/
|
||||
@Override
|
||||
public void prepare() {
|
||||
executor.submit(() -> {
|
||||
playerLock.lock();
|
||||
|
||||
if (playerStatus == PlayerStatus.INITIALIZED) {
|
||||
Log.d(TAG, "Preparing media player");
|
||||
setPlayerStatus(PlayerStatus.PREPARING, media);
|
||||
try {
|
||||
mediaPlayer.prepare();
|
||||
onPrepared(startWhenPrepared.get());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
setPlayerStatus(PlayerStatus.ERROR, null);
|
||||
EventBus.getDefault().postSticky(new PlayerErrorEvent(e.getLocalizedMessage()));
|
||||
}
|
||||
}
|
||||
playerLock.unlock();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after media player has been prepared. This method is executed on the caller's thread.
|
||||
*/
|
||||
private void onPrepared(final boolean startWhenPrepared) {
|
||||
playerLock.lock();
|
||||
|
||||
if (playerStatus != PlayerStatus.PREPARING) {
|
||||
playerLock.unlock();
|
||||
throw new IllegalStateException("Player is not in PREPARING state");
|
||||
}
|
||||
|
||||
Log.d(TAG, "Resource prepared");
|
||||
|
||||
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());
|
||||
if (mediaType == MediaType.VIDEO) {
|
||||
videoSize = new Pair<>(mediaPlayer.getVideoWidth(), mediaPlayer.getVideoHeight());
|
||||
}
|
||||
|
||||
// TODO this call has no effect!
|
||||
if (media.getPosition() > 0) {
|
||||
seekToSync(media.getPosition());
|
||||
seekTo(media.getPosition());
|
||||
}
|
||||
|
||||
if (media.getDuration() <= 0) {
|
||||
|
@ -451,10 +309,8 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
setPlayerStatus(PlayerStatus.PREPARED, media);
|
||||
|
||||
if (startWhenPrepared) {
|
||||
resumeSync();
|
||||
resume();
|
||||
}
|
||||
|
||||
playerLock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -464,9 +320,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
*/
|
||||
@Override
|
||||
public void reinit() {
|
||||
useCallerThread = UserPreferences.useExoplayer();
|
||||
executor.submit(() -> {
|
||||
playerLock.lock();
|
||||
Log.d(TAG, "reinit()");
|
||||
releaseWifiLockIfNecessary();
|
||||
if (media != null) {
|
||||
|
@ -476,23 +329,19 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
} else {
|
||||
Log.d(TAG, "Call to reinit was ignored: media and mediaPlayer were null");
|
||||
}
|
||||
playerLock.unlock();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Seeks to the specified position. If the PSMP object is in an invalid state, this method will do nothing.
|
||||
*
|
||||
* @param t The position to seek to in milliseconds. t < 0 will be interpreted as t = 0
|
||||
* Invalid time values (< 0) will be ignored.
|
||||
* <p/>
|
||||
* This method is executed on the caller's thread.
|
||||
* This method is executed on an internal executor service.
|
||||
*/
|
||||
private void seekToSync(int t) {
|
||||
@Override
|
||||
public void seekTo(int t) {
|
||||
if (t < 0) {
|
||||
t = 0;
|
||||
}
|
||||
playerLock.lock();
|
||||
|
||||
if (playerStatus == PlayerStatus.PLAYING
|
||||
|| playerStatus == PlayerStatus.PAUSED
|
||||
|
@ -521,18 +370,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
startWhenPrepared.set(false);
|
||||
prepare();
|
||||
}
|
||||
playerLock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Seeks to the specified position. If the PSMP object is in an invalid state, this method will do nothing.
|
||||
* Invalid time values (< 0) will be ignored.
|
||||
* <p/>
|
||||
* This method is executed on an internal executor service.
|
||||
*/
|
||||
@Override
|
||||
public void seekTo(final int t) {
|
||||
executor.submit(() -> seekToSync(t));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -542,17 +379,12 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
*/
|
||||
@Override
|
||||
public void seekDelta(final int d) {
|
||||
executor.submit(() -> {
|
||||
playerLock.lock();
|
||||
int currentPosition = getPosition();
|
||||
if (currentPosition != Playable.INVALID_TIME) {
|
||||
seekToSync(currentPosition + d);
|
||||
seekTo(currentPosition + d);
|
||||
} else {
|
||||
Log.e(TAG, "getPosition() returned INVALID_TIME in seekDelta");
|
||||
}
|
||||
|
||||
playerLock.unlock();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -560,10 +392,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
*/
|
||||
@Override
|
||||
public int getDuration() {
|
||||
if (!playerLock.tryLock()) {
|
||||
return Playable.INVALID_TIME;
|
||||
}
|
||||
|
||||
int retVal = Playable.INVALID_TIME;
|
||||
if (playerStatus == PlayerStatus.PLAYING
|
||||
|| playerStatus == PlayerStatus.PAUSED
|
||||
|
@ -573,8 +401,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
if (retVal <= 0 && media != null && media.getDuration() > 0) {
|
||||
retVal = media.getDuration();
|
||||
}
|
||||
|
||||
playerLock.unlock();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -583,14 +409,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
*/
|
||||
@Override
|
||||
public int getPosition() {
|
||||
try {
|
||||
if (!playerLock.tryLock(50, TimeUnit.MILLISECONDS)) {
|
||||
return Playable.INVALID_TIME;
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
return Playable.INVALID_TIME;
|
||||
}
|
||||
|
||||
int retVal = Playable.INVALID_TIME;
|
||||
if (playerStatus.isAtLeast(PlayerStatus.PREPARED)) {
|
||||
retVal = mediaPlayer.getCurrentPosition();
|
||||
|
@ -598,8 +416,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
if (retVal <= 0 && media != null && media.getPosition() >= 0) {
|
||||
retVal = media.getPosition();
|
||||
}
|
||||
|
||||
playerLock.unlock();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -613,25 +429,15 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
this.startWhenPrepared.set(startWhenPrepared);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the playback speed.
|
||||
* This method is executed on the caller's thread.
|
||||
*/
|
||||
private void setSpeedSyncAndSkipSilence(float speed, boolean skipSilence) {
|
||||
playerLock.lock();
|
||||
Log.d(TAG, "Playback speed was set to " + speed);
|
||||
EventBus.getDefault().post(new SpeedChangedEvent(speed));
|
||||
mediaPlayer.setPlaybackParams(speed, skipSilence);
|
||||
playerLock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the playback speed.
|
||||
* This method is executed on an internal executor service.
|
||||
*/
|
||||
@Override
|
||||
public void setPlaybackParams(final float speed, final boolean skipSilence) {
|
||||
executor.submit(() -> setSpeedSyncAndSkipSilence(speed, skipSilence));
|
||||
Log.d(TAG, "Playback speed was set to " + speed);
|
||||
EventBus.getDefault().post(new SpeedChangedEvent(speed));
|
||||
mediaPlayer.setPlaybackParams(speed, skipSilence);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -639,10 +445,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
*/
|
||||
@Override
|
||||
public float getPlaybackSpeed() {
|
||||
if (!playerLock.tryLock()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
float retVal = 1;
|
||||
if ((playerStatus == PlayerStatus.PLAYING
|
||||
|| playerStatus == PlayerStatus.PAUSED
|
||||
|
@ -650,7 +452,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
|| playerStatus == PlayerStatus.PREPARED)) {
|
||||
retVal = mediaPlayer.getCurrentSpeedMultiplier();
|
||||
}
|
||||
playerLock.unlock();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -659,16 +460,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
* This method is executed on an internal executor service.
|
||||
*/
|
||||
@Override
|
||||
public void setVolume(final float volumeLeft, float volumeRight) {
|
||||
executor.submit(() -> setVolumeSync(volumeLeft, volumeRight));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the playback volume.
|
||||
* This method is executed on the caller's thread.
|
||||
*/
|
||||
private void setVolumeSync(float volumeLeft, float volumeRight) {
|
||||
playerLock.lock();
|
||||
public void setVolume(float volumeLeft, float volumeRight) {
|
||||
Playable playable = getPlayable();
|
||||
if (playable instanceof FeedMedia) {
|
||||
FeedMedia feedMedia = (FeedMedia) playable;
|
||||
|
@ -680,29 +472,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
}
|
||||
mediaPlayer.setVolume(volumeLeft, volumeRight);
|
||||
Log.d(TAG, "Media player volume was set to " + volumeLeft + " " + volumeRight);
|
||||
playerLock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the mediaplayer can mix stereo down to mono
|
||||
*/
|
||||
@Override
|
||||
public boolean canDownmix() {
|
||||
boolean retVal = false;
|
||||
if (mediaPlayer != null && media != null && media.getMediaType() == MediaType.AUDIO) {
|
||||
retVal = mediaPlayer.canDownmix();
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDownmix(boolean enable) {
|
||||
playerLock.lock();
|
||||
if (media != null && media.getMediaType() == MediaType.AUDIO) {
|
||||
mediaPlayer.setDownmix(enable);
|
||||
Log.d(TAG, "Media player downmix was set to " + enable);
|
||||
}
|
||||
playerLock.unlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -734,26 +503,19 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
playerStatus = PlayerStatus.STOPPED;
|
||||
}
|
||||
isShutDown = true;
|
||||
executor.shutdown();
|
||||
abandonAudioFocus();
|
||||
releaseWifiLockIfNecessary();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVideoSurface(final SurfaceHolder surface) {
|
||||
executor.submit(() -> {
|
||||
playerLock.lock();
|
||||
if (mediaPlayer != null) {
|
||||
mediaPlayer.setDisplay(surface);
|
||||
}
|
||||
playerLock.unlock();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetVideoSurface() {
|
||||
executor.submit(() -> {
|
||||
playerLock.lock();
|
||||
if (mediaType == MediaType.VIDEO) {
|
||||
Log.d(TAG, "Resetting video surface");
|
||||
mediaPlayer.setDisplay(null);
|
||||
|
@ -761,8 +523,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
} else {
|
||||
Log.e(TAG, "Resetting video surface for media of Audio type");
|
||||
}
|
||||
playerLock.unlock();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -774,25 +534,11 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
*/
|
||||
@Override
|
||||
public Pair<Integer, Integer> getVideoSize() {
|
||||
if (!playerLock.tryLock()) {
|
||||
// use cached value if lock can't be aquired
|
||||
if (mediaPlayer != null && playerStatus != PlayerStatus.ERROR && mediaType == MediaType.VIDEO) {
|
||||
videoSize = new Pair<>(mediaPlayer.getVideoWidth(), mediaPlayer.getVideoHeight());
|
||||
}
|
||||
return videoSize;
|
||||
}
|
||||
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());
|
||||
res = videoSize;
|
||||
}
|
||||
playerLock.unlock();
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current media, if you need the media and the player status together, you should
|
||||
|
@ -832,16 +578,8 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -858,8 +596,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
return;
|
||||
}
|
||||
|
||||
executor.submit(() -> {
|
||||
playerLock.lock();
|
||||
if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
|
||||
Log.d(TAG, "Lost audio focus");
|
||||
pause(true, false);
|
||||
|
@ -868,7 +604,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
&& !UserPreferences.shouldPauseForFocusLoss()) {
|
||||
if (playerStatus == PlayerStatus.PLAYING) {
|
||||
Log.d(TAG, "Lost audio focus temporarily. Ducking...");
|
||||
setVolumeSync(0.25f, 0.25f);
|
||||
setVolume(0.25f, 0.25f);
|
||||
pausedBecauseOfTransientAudiofocusLoss = false;
|
||||
}
|
||||
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
|
||||
|
@ -892,22 +628,17 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
if (pausedBecauseOfTransientAudiofocusLoss) { // we paused => play now
|
||||
mediaPlayer.start();
|
||||
} else { // we ducked => raise audio level back
|
||||
setVolumeSync(1.0f, 1.0f);
|
||||
setVolume(1.0f, 1.0f);
|
||||
}
|
||||
pausedBecauseOfTransientAudiofocusLoss = false;
|
||||
}
|
||||
playerLock.unlock();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@Override
|
||||
protected Future<?> endPlayback(final boolean hasEnded, final boolean wasSkipped,
|
||||
protected void endPlayback(final boolean hasEnded, final boolean wasSkipped,
|
||||
final boolean shouldContinue, final boolean toStoppedState) {
|
||||
useCallerThread = UserPreferences.useExoplayer();
|
||||
return executor.submit(() -> {
|
||||
playerLock.lock();
|
||||
releaseWifiLockIfNecessary();
|
||||
|
||||
boolean isPlaying = playerStatus == PlayerStatus.PLAYING;
|
||||
|
@ -949,12 +680,10 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
}
|
||||
final boolean hasNext = nextMedia != null;
|
||||
|
||||
executor.submit(() -> callback.onPostPlayback(currentMedia, hasEnded, wasSkipped, hasNext));
|
||||
callback.onPostPlayback(currentMedia, hasEnded, wasSkipped, hasNext);
|
||||
} else if (isPlaying) {
|
||||
callback.onPlaybackPause(currentMedia, currentMedia.getPosition());
|
||||
}
|
||||
playerLock.unlock();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -964,8 +693,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
* abandoning audio focus have to be done with other methods.
|
||||
*/
|
||||
private void stop() {
|
||||
executor.submit(() -> {
|
||||
playerLock.lock();
|
||||
releaseWifiLockIfNecessary();
|
||||
|
||||
if (playerStatus == PlayerStatus.INDETERMINATE) {
|
||||
|
@ -973,9 +700,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
} else {
|
||||
Log.d(TAG, "Ignored call to stop: Current player state is: " + playerStatus);
|
||||
}
|
||||
playerLock.unlock();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -983,147 +707,42 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
return stream;
|
||||
}
|
||||
|
||||
private void setMediaPlayerListeners(IPlayer mp) {
|
||||
private void setMediaPlayerListeners(ExoPlayerWrapper mp) {
|
||||
if (mp == null || media == null) {
|
||||
return;
|
||||
}
|
||||
if (mp instanceof VideoPlayer) {
|
||||
if (media.getMediaType() != MediaType.VIDEO) {
|
||||
Log.w(TAG, "video player, but media type is " + media.getMediaType());
|
||||
}
|
||||
VideoPlayer vp = (VideoPlayer) mp;
|
||||
vp.setOnCompletionListener(videoCompletionListener);
|
||||
vp.setOnSeekCompleteListener(videoSeekCompleteListener);
|
||||
vp.setOnErrorListener(videoErrorListener);
|
||||
vp.setOnBufferingUpdateListener(videoBufferingUpdateListener);
|
||||
vp.setOnInfoListener(videoInfoListener);
|
||||
} else if (mp instanceof AudioPlayer) {
|
||||
if (media.getMediaType() != MediaType.AUDIO) {
|
||||
Log.w(TAG, "audio player, but media type is " + media.getMediaType());
|
||||
}
|
||||
AudioPlayer ap = (AudioPlayer) mp;
|
||||
ap.setOnCompletionListener(audioCompletionListener);
|
||||
ap.setOnSeekCompleteListener(audioSeekCompleteListener);
|
||||
ap.setOnErrorListener(audioErrorListener);
|
||||
ap.setOnBufferingUpdateListener(audioBufferingUpdateListener);
|
||||
ap.setOnInfoListener(audioInfoListener);
|
||||
} else if (mp instanceof ExoPlayerWrapper) {
|
||||
ExoPlayerWrapper ap = (ExoPlayerWrapper) mp;
|
||||
ap.setOnCompletionListener(audioCompletionListener);
|
||||
ap.setOnSeekCompleteListener(audioSeekCompleteListener);
|
||||
ap.setOnBufferingUpdateListener(audioBufferingUpdateListener);
|
||||
ap.setOnErrorListener(message -> EventBus.getDefault().postSticky(new PlayerErrorEvent(message)));
|
||||
ap.setOnInfoListener(audioInfoListener);
|
||||
mp.setOnCompletionListener(() -> endPlayback(true, false, true, true));
|
||||
mp.setOnSeekCompleteListener(this::genericSeekCompleteListener);
|
||||
mp.setOnBufferingUpdateListener(percent -> {
|
||||
if (percent == ExoPlayerWrapper.BUFFERING_STARTED) {
|
||||
EventBus.getDefault().post(BufferUpdateEvent.started());
|
||||
} else if (percent == ExoPlayerWrapper.BUFFERING_ENDED) {
|
||||
EventBus.getDefault().post(BufferUpdateEvent.ended());
|
||||
} else {
|
||||
Log.w(TAG, "Unknown media player: " + mp);
|
||||
EventBus.getDefault().post(BufferUpdateEvent.progressUpdate(0.01f * percent));
|
||||
}
|
||||
});
|
||||
mp.setOnErrorListener(message -> EventBus.getDefault().postSticky(new PlayerErrorEvent(message)));
|
||||
}
|
||||
|
||||
private void clearMediaPlayerListeners() {
|
||||
if (mediaPlayer instanceof VideoPlayer) {
|
||||
VideoPlayer vp = (VideoPlayer) mediaPlayer;
|
||||
vp.setOnCompletionListener(x -> { });
|
||||
vp.setOnSeekCompleteListener(x -> { });
|
||||
vp.setOnErrorListener((mediaPlayer, i, i1) -> false);
|
||||
vp.setOnBufferingUpdateListener((mediaPlayer, i) -> { });
|
||||
vp.setOnInfoListener((mediaPlayer, i, i1) -> false);
|
||||
} else if (mediaPlayer instanceof AudioPlayer) {
|
||||
AudioPlayer ap = (AudioPlayer) mediaPlayer;
|
||||
ap.setOnCompletionListener(x -> { });
|
||||
ap.setOnSeekCompleteListener(x -> { });
|
||||
ap.setOnErrorListener((x, y, z) -> false);
|
||||
ap.setOnBufferingUpdateListener((arg0, percent) -> { });
|
||||
ap.setOnInfoListener((arg0, what, extra) -> false);
|
||||
} else if (mediaPlayer instanceof ExoPlayerWrapper) {
|
||||
ExoPlayerWrapper ap = (ExoPlayerWrapper) mediaPlayer;
|
||||
ap.setOnCompletionListener(x -> { });
|
||||
ap.setOnSeekCompleteListener(x -> { });
|
||||
ap.setOnBufferingUpdateListener((arg0, percent) -> { });
|
||||
ap.setOnErrorListener(x -> { });
|
||||
ap.setOnInfoListener((arg0, what, extra) -> false);
|
||||
mediaPlayer.setOnCompletionListener(() -> { });
|
||||
mediaPlayer.setOnSeekCompleteListener(() -> { });
|
||||
mediaPlayer.setOnBufferingUpdateListener(percent -> { });
|
||||
mediaPlayer.setOnErrorListener(x -> { });
|
||||
}
|
||||
}
|
||||
|
||||
private final MediaPlayer.OnCompletionListener audioCompletionListener =
|
||||
mp -> genericOnCompletion();
|
||||
|
||||
private final android.media.MediaPlayer.OnCompletionListener videoCompletionListener =
|
||||
mp -> genericOnCompletion();
|
||||
|
||||
private void genericOnCompletion() {
|
||||
endPlayback(true, false, true, true);
|
||||
}
|
||||
|
||||
private final MediaPlayer.OnBufferingUpdateListener audioBufferingUpdateListener =
|
||||
(mp, percent) -> EventBus.getDefault().post(BufferUpdateEvent.progressUpdate(0.01f * percent));
|
||||
|
||||
private final android.media.MediaPlayer.OnBufferingUpdateListener videoBufferingUpdateListener =
|
||||
(mp, percent) -> EventBus.getDefault().post(BufferUpdateEvent.progressUpdate(0.01f * percent));
|
||||
|
||||
private final MediaPlayer.OnInfoListener audioInfoListener =
|
||||
(mp, what, extra) -> genericInfoListener(what);
|
||||
|
||||
private final android.media.MediaPlayer.OnInfoListener videoInfoListener =
|
||||
(mp, what, extra) -> genericInfoListener(what);
|
||||
|
||||
private boolean genericInfoListener(int what) {
|
||||
switch (what) {
|
||||
case android.media.MediaPlayer.MEDIA_INFO_BUFFERING_START:
|
||||
EventBus.getDefault().post(BufferUpdateEvent.started());
|
||||
return true;
|
||||
case android.media.MediaPlayer.MEDIA_INFO_BUFFERING_END:
|
||||
EventBus.getDefault().post(BufferUpdateEvent.ended());
|
||||
return true;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private final MediaPlayer.OnErrorListener audioErrorListener =
|
||||
(mp, what, extra) -> {
|
||||
if(mp != null && mp.canFallback()) {
|
||||
mp.fallback();
|
||||
return true;
|
||||
} else {
|
||||
return genericOnError(mp, what, extra);
|
||||
}
|
||||
};
|
||||
|
||||
private final android.media.MediaPlayer.OnErrorListener videoErrorListener = this::genericOnError;
|
||||
|
||||
private boolean genericOnError(Object inObj, int what, int extra) {
|
||||
EventBus.getDefault().postSticky(new PlayerErrorEvent(MediaPlayerError.getErrorString(context, what)));
|
||||
return true;
|
||||
}
|
||||
|
||||
private final MediaPlayer.OnSeekCompleteListener audioSeekCompleteListener =
|
||||
mp -> genericSeekCompleteListener();
|
||||
|
||||
private final android.media.MediaPlayer.OnSeekCompleteListener videoSeekCompleteListener =
|
||||
mp -> genericSeekCompleteListener();
|
||||
|
||||
private void genericSeekCompleteListener() {
|
||||
Log.d(TAG, "genericSeekCompleteListener");
|
||||
if (seekLatch != null) {
|
||||
seekLatch.countDown();
|
||||
}
|
||||
|
||||
Runnable r = () -> {
|
||||
playerLock.lock();
|
||||
if (playerStatus == PlayerStatus.PLAYING) {
|
||||
callback.onPlaybackStart(media, getPosition());
|
||||
}
|
||||
if (playerStatus == PlayerStatus.SEEKING) {
|
||||
setPlayerStatus(statusBeforeSeeking, media, getPosition());
|
||||
}
|
||||
playerLock.unlock();
|
||||
};
|
||||
|
||||
if (useCallerThread) {
|
||||
r.run();
|
||||
} else {
|
||||
executor.submit(r);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1578,14 +1578,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
return mediaPlayer.getPlaybackSpeed();
|
||||
}
|
||||
|
||||
public boolean canDownmix() {
|
||||
return mediaPlayer.canDownmix();
|
||||
}
|
||||
|
||||
public void setDownmix(boolean enable) {
|
||||
mediaPlayer.setDownmix(enable);
|
||||
}
|
||||
|
||||
public boolean isStartWhenPrepared() {
|
||||
return mediaPlayer.isStartWhenPrepared();
|
||||
}
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
package de.danoeh.antennapod.core.util.playback;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceHolder;
|
||||
|
||||
import de.danoeh.antennapod.core.ClientConfig;
|
||||
import org.antennapod.audio.MediaPlayer;
|
||||
|
||||
import de.danoeh.antennapod.storage.preferences.UserPreferences;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class AudioPlayer extends MediaPlayer implements IPlayer {
|
||||
private static final String TAG = "AudioPlayer";
|
||||
|
||||
public AudioPlayer(Context context) {
|
||||
super(context, true, ClientConfig.USER_AGENT);
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.registerOnSharedPreferenceChangeListener((sharedPreferences, key) -> {
|
||||
if (UserPreferences.PREF_MEDIA_PLAYER.equals(key)) {
|
||||
checkMpi();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplay(SurfaceHolder sh) {
|
||||
if (sh != null) {
|
||||
Log.e(TAG, "Setting display not supported in Audio Player");
|
||||
throw new UnsupportedOperationException("Setting display not supported in Audio Player");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlaybackParams(float speed, boolean skipSilence) {
|
||||
if (canSetSpeed()) {
|
||||
try {
|
||||
setPlaybackSpeed(speed);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
//Default player does not support silence skipping
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean useSonic() {
|
||||
return UserPreferences.useSonic();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean downmix() {
|
||||
return UserPreferences.stereoToMono();
|
||||
}
|
||||
|
||||
public List<String> getAudioTracks() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAudioTrack(int track) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSelectedAudioTrack() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataSource(String streamUrl, String username, String password) throws IOException {
|
||||
setDataSource(streamUrl);
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
package de.danoeh.antennapod.core.util.playback;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.SurfaceHolder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public interface IPlayer {
|
||||
boolean canDownmix();
|
||||
|
||||
int getCurrentPosition();
|
||||
|
||||
float getCurrentSpeedMultiplier();
|
||||
|
||||
int getDuration();
|
||||
|
||||
boolean isPlaying();
|
||||
|
||||
void pause();
|
||||
|
||||
void prepare() throws IllegalStateException, IOException;
|
||||
|
||||
void release();
|
||||
|
||||
void reset();
|
||||
|
||||
void seekTo(int msec);
|
||||
|
||||
void setAudioStreamType(int streamtype);
|
||||
|
||||
void setDataSource(String path) throws IllegalStateException, IOException,
|
||||
IllegalArgumentException, SecurityException;
|
||||
|
||||
void setDataSource(String streamUrl, String username, String password) throws IOException;
|
||||
|
||||
void setDisplay(SurfaceHolder sh);
|
||||
|
||||
void setPlaybackParams(float speed, boolean skipSilence);
|
||||
|
||||
void setDownmix(boolean enable);
|
||||
|
||||
void setVolume(float left, float right);
|
||||
|
||||
void start();
|
||||
|
||||
void stop();
|
||||
|
||||
void setWakeMode(Context context, int mode);
|
||||
|
||||
List<String> getAudioTracks();
|
||||
|
||||
void setAudioTrack(int track);
|
||||
|
||||
int getSelectedAudioTrack();
|
||||
}
|
|
@ -21,7 +21,6 @@ import de.danoeh.antennapod.event.playback.SpeedChangedEvent;
|
|||
import de.danoeh.antennapod.model.playback.MediaType;
|
||||
import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
|
||||
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
|
||||
import de.danoeh.antennapod.storage.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||
import de.danoeh.antennapod.model.playback.Playable;
|
||||
import de.danoeh.antennapod.playback.base.PlaybackServiceMediaPlayer;
|
||||
|
@ -420,17 +419,6 @@ public abstract class PlaybackController {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean canDownmix() {
|
||||
return (playbackService != null && playbackService.canDownmix())
|
||||
|| UserPreferences.useSonic();
|
||||
}
|
||||
|
||||
public void setDownmix(boolean enable) {
|
||||
if (playbackService != null) {
|
||||
playbackService.setDownmix(enable);
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getAudioTracks() {
|
||||
if (playbackService == null) {
|
||||
return Collections.emptyList();
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
package de.danoeh.antennapod.core.util.playback;
|
||||
|
||||
import android.media.MediaPlayer;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class VideoPlayer extends MediaPlayer implements IPlayer {
|
||||
private static final String TAG = "VideoPlayer";
|
||||
|
||||
@Override
|
||||
public boolean canDownmix() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getCurrentSpeedMultiplier() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlaybackParams(float speed, boolean skipSilence) {
|
||||
//Ignore this for non ExoPlayer implementations
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDownmix(boolean b) {
|
||||
Log.e(TAG, "Setting downmix unsupported in video player");
|
||||
throw new UnsupportedOperationException("Setting downmix unsupported in video player");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVideoScalingMode(int mode) {
|
||||
super.setVideoScalingMode(mode);
|
||||
}
|
||||
|
||||
public List<String> getAudioTracks() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAudioTrack(int track) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSelectedAudioTrack() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataSource(String streamUrl, String username, String password) throws IOException {
|
||||
setDataSource(streamUrl);
|
||||
}
|
||||
}
|
|
@ -216,18 +216,6 @@
|
|||
<item>DownloadsSection</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="media_player_options">
|
||||
<item>@string/media_player_exoplayer_recommended</item>
|
||||
<item>@string/media_player_builtin</item>
|
||||
<item>@string/media_player_sonic</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="media_player_values">
|
||||
<item>exoplayer</item>
|
||||
<item>builtin</item>
|
||||
<item>sonic</item>
|
||||
</string-array>
|
||||
|
||||
<!-- sort for podcast screen, not for queue -->
|
||||
<string-array name="feed_episodes_sort_options">
|
||||
<item>@string/sort_date_new_old</item>
|
||||
|
|
|
@ -9,7 +9,6 @@ import android.util.Pair;
|
|||
import android.view.SurfaceHolder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import de.danoeh.antennapod.model.playback.MediaType;
|
||||
|
@ -158,13 +157,6 @@ public abstract class PlaybackServiceMediaPlayer {
|
|||
*/
|
||||
public abstract void setVolume(float volumeLeft, float volumeRight);
|
||||
|
||||
/**
|
||||
* Returns true if the mediaplayer can mix stereo down to mono
|
||||
*/
|
||||
public abstract boolean canDownmix();
|
||||
|
||||
public abstract void setDownmix(boolean enable);
|
||||
|
||||
public abstract MediaType getCurrentMediaType();
|
||||
|
||||
public abstract boolean isStreaming();
|
||||
|
@ -232,8 +224,8 @@ public abstract class PlaybackServiceMediaPlayer {
|
|||
*
|
||||
* @see #endPlayback(boolean, boolean, boolean, boolean)
|
||||
*/
|
||||
public Future<?> stopPlayback(boolean toStoppedState) {
|
||||
return endPlayback(false, false, false, toStoppedState);
|
||||
public void stopPlayback(boolean toStoppedState) {
|
||||
endPlayback(false, false, false, toStoppedState);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -262,7 +254,7 @@ public abstract class PlaybackServiceMediaPlayer {
|
|||
*
|
||||
* @return a Future, just for the purpose of tracking its execution.
|
||||
*/
|
||||
protected abstract Future<?> endPlayback(boolean hasEnded, boolean wasSkipped,
|
||||
protected abstract void endPlayback(boolean hasEnded, boolean wasSkipped,
|
||||
boolean shouldContinue, boolean toStoppedState);
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,8 +8,6 @@ import android.view.SurfaceHolder;
|
|||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
@ -430,16 +428,6 @@ public class CastPsmp extends PlaybackServiceMediaPlayer {
|
|||
remoteMediaClient.setStreamVolume(volumeLeft);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDownmix() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDownmix(boolean enable) {
|
||||
throw new UnsupportedOperationException("Setting downmix unsupported in Remote Media Player");
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaType getCurrentMediaType() {
|
||||
return mediaType;
|
||||
|
@ -497,7 +485,7 @@ public class CastPsmp extends PlaybackServiceMediaPlayer {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Future<?> endPlayback(boolean hasEnded, boolean wasSkipped, boolean shouldContinue,
|
||||
protected void endPlayback(boolean hasEnded, boolean wasSkipped, boolean shouldContinue,
|
||||
boolean toStoppedState) {
|
||||
Log.d(TAG, "endPlayback() called");
|
||||
boolean isPlaying = playerStatus == PlayerStatus.PLAYING;
|
||||
|
@ -544,10 +532,6 @@ public class CastPsmp extends PlaybackServiceMediaPlayer {
|
|||
callback.onPlaybackPause(currentMedia,
|
||||
currentMedia != null ? currentMedia.getPosition() : Playable.INVALID_TIME);
|
||||
}
|
||||
|
||||
FutureTask<?> future = new FutureTask<>(() -> { }, null);
|
||||
future.run();
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -112,11 +112,8 @@ public class UserPreferences {
|
|||
// Other
|
||||
private static final String PREF_DATA_FOLDER = "prefDataFolder";
|
||||
public static final String PREF_DELETE_REMOVES_FROM_QUEUE = "prefDeleteRemovesFromQueue";
|
||||
public static final String PREF_USAGE_COUNTING_DATE = "prefUsageCounting";
|
||||
|
||||
// Mediaplayer
|
||||
public static final String PREF_MEDIA_PLAYER = "prefMediaPlayer";
|
||||
public static final String PREF_MEDIA_PLAYER_EXOPLAYER = "exoplayer";
|
||||
private static final String PREF_PLAYBACK_SPEED = "prefPlaybackSpeed";
|
||||
private static final String PREF_VIDEO_PLAYBACK_SPEED = "prefVideoPlaybackSpeed";
|
||||
public static final String PREF_PLAYBACK_SKIP_SILENCE = "prefSkipSilence";
|
||||
|
@ -125,7 +122,6 @@ public class UserPreferences {
|
|||
private static final String PREF_QUEUE_LOCKED = "prefQueueLocked";
|
||||
|
||||
// Experimental
|
||||
private static final String PREF_STEREO_TO_MONO = "PrefStereoToMono";
|
||||
public static final int EPISODE_CLEANUP_QUEUE = -1;
|
||||
public static final int EPISODE_CLEANUP_NULL = -2;
|
||||
public static final int EPISODE_CLEANUP_EXCEPT_FAVORITE = -3;
|
||||
|
@ -780,32 +776,6 @@ public class UserPreferences {
|
|||
return Arrays.asList(1.0f, 1.25f, 1.5f);
|
||||
}
|
||||
|
||||
public static String getMediaPlayer() {
|
||||
return prefs.getString(PREF_MEDIA_PLAYER, PREF_MEDIA_PLAYER_EXOPLAYER);
|
||||
}
|
||||
|
||||
public static boolean useSonic() {
|
||||
return getMediaPlayer().equals("sonic");
|
||||
}
|
||||
|
||||
public static boolean useExoplayer() {
|
||||
return getMediaPlayer().equals(PREF_MEDIA_PLAYER_EXOPLAYER);
|
||||
}
|
||||
|
||||
public static void enableExoplayer() {
|
||||
prefs.edit().putString(PREF_MEDIA_PLAYER, PREF_MEDIA_PLAYER_EXOPLAYER).apply();
|
||||
}
|
||||
|
||||
public static boolean stereoToMono() {
|
||||
return prefs.getBoolean(PREF_STEREO_TO_MONO, false);
|
||||
}
|
||||
|
||||
public static void stereoToMono(boolean enable) {
|
||||
prefs.edit()
|
||||
.putBoolean(PREF_STEREO_TO_MONO, enable)
|
||||
.apply();
|
||||
}
|
||||
|
||||
public static int getEpisodeCleanupValue() {
|
||||
return Integer.parseInt(prefs.getString(PREF_EPISODE_CLEANUP, "" + EPISODE_CLEANUP_NULL));
|
||||
}
|
||||
|
|
|
@ -385,7 +385,6 @@
|
|||
<string name="preference_search_hint">Search…</string>
|
||||
<string name="preference_search_no_results">No results</string>
|
||||
<string name="preference_search_clear_history">Clear history</string>
|
||||
<string name="media_player">Media player</string>
|
||||
<string name="pref_episode_cleanup_title">Episode Cleanup</string>
|
||||
<string name="pref_episode_cleanup_summary">Episodes that should be eligible for removal if Auto Download needs space for new episodes</string>
|
||||
<string name="pref_pauseOnDisconnect_sum">Pause playback when headphones or bluetooth are disconnected</string>
|
||||
|
@ -500,19 +499,12 @@
|
|||
<string name="open_bug_tracker">Open bug tracker</string>
|
||||
<string name="copy_to_clipboard">Copy to clipboard</string>
|
||||
<string name="copied_to_clipboard">Copied to clipboard</string>
|
||||
<string name="experimental_pref">Experimental</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>
|
||||
<string name="pref_no_browser_found">No web browser found.</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 (deprecated) </string>
|
||||
<string name="media_player_sonic">Sonic Media Player (deprecated) </string>
|
||||
<string name="media_player_exoplayer_recommended">ExoPlayer (recommended)</string>
|
||||
<string name="media_player_switch_to_exoplayer">Switch to ExoPlayer</string>
|
||||
<string name="media_player_switched_to_exoplayer">Switched to ExoPlayer.</string>
|
||||
<string name="pref_skip_silence_title">Skip Silence in Audio</string>
|
||||
<string name="behavior">Behavior</string>
|
||||
<string name="pref_default_page">Default Page</string>
|
||||
|
@ -800,9 +792,6 @@
|
|||
<string name="audio_controls">Audio controls</string>
|
||||
<string name="playback_speed">Playback Speed</string>
|
||||
<string name="audio_effects">Audio Effects</string>
|
||||
<string name="stereo_to_mono">Downmix: Stereo to mono</string>
|
||||
<string name="sonic_only">Sonic only</string>
|
||||
<string name="exoplayer_only">ExoPlayer only</string>
|
||||
<string name="player_switch_to_audio_only">Switch to audio only</string>
|
||||
|
||||
<!-- proxy settings -->
|
||||
|
|
Loading…
Reference in New Issue