adapt media player switch into the new protocol

This commit is contained in:
Domingos Lopes 2016-05-13 01:33:05 -04:00
parent 385079d168
commit c17723816b
5 changed files with 124 additions and 63 deletions

View File

@ -13,6 +13,7 @@ import org.antennapod.audio.MediaPlayer;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@ -754,8 +755,8 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
@Override
protected void endPlayback(final boolean wasSkipped) {
executor.submit(() -> {
protected Future<?> endPlayback(final boolean wasSkipped, final boolean shouldContinue, final boolean toStoppedState) {
return executor.submit(() -> {
playerLock.lock();
releaseWifiLockIfNecessary();
@ -776,35 +777,46 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
mediaPlayer.reset();
}
audioManager.abandonAudioFocus(audioFocusChangeListener);
// Load next episode if previous episode was in the queue and if there
// is an episode in the queue left.
// Start playback immediately if continuous playback is enabled
final Playable currentMedia = media;
Playable nextMedia = callback.getNextInQueue(currentMedia);
Playable nextMedia = null;
boolean playNextEpisode = isPlaying &&
nextMedia != null &&
UserPreferences.isFollowQueue();
if (shouldContinue) {
// Load next episode if previous episode was in the queue and if there
// is an episode in the queue left.
// Start playback immediately if continuous playback is enabled
nextMedia = callback.getNextInQueue(currentMedia);
if (playNextEpisode) {
Log.d(TAG, "Playback of next episode will start immediately.");
} else if (nextMedia == null){
Log.d(TAG, "No more episodes available to play");
} else {
Log.d(TAG, "Loading next episode, but not playing automatically.");
boolean playNextEpisode = isPlaying &&
nextMedia != null &&
UserPreferences.isFollowQueue();
if (playNextEpisode) {
Log.d(TAG, "Playback of next episode will start immediately.");
} else if (nextMedia == null){
Log.d(TAG, "No more episodes available to play");
} else {
Log.d(TAG, "Loading next episode, but not playing automatically.");
}
if (nextMedia != null) {
callback.onPlaybackEnded(nextMedia.getMediaType(), !playNextEpisode);
// setting media to null signals to playMediaObject() that we're taking care of post-playback processing
media = null;
playMediaObject(nextMedia, false, !nextMedia.localFileAvailable(), playNextEpisode, playNextEpisode);
}
}
if (shouldContinue || toStoppedState) {
if (nextMedia == null) {
callback.onPlaybackEnded(null, true);
stop();
}
final boolean hasNext = nextMedia != null;
if (nextMedia != null) {
callback.onPlaybackEnded(nextMedia.getMediaType(), !playNextEpisode);
// setting media to null signals to playMediaObject() that we're taking care of post-playback processing
media = null;
playMediaObject(nextMedia, false, !nextMedia.localFileAvailable(), playNextEpisode, playNextEpisode);
} else {
callback.onPlaybackEnded(null, true);
stop();
executor.submit(() -> callback.onPostPlayback(currentMedia, !wasSkipped, hasNext));
} else if (isPlaying) {
callback.onPlaybackPause(currentMedia, currentMedia.getPosition());
}
executor.submit(() -> callback.onPostPlayback(currentMedia, !wasSkipped, nextMedia != null));
playerLock.unlock();
});
}
@ -815,8 +827,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
* This method will only take care of changing the PlayerStatus of this object! Other tasks like
* abandoning audio focus have to be done with other methods.
*/
@Override
public void stop() {
private void stop() {
executor.submit(() -> {
playerLock.lock();
releaseWifiLockIfNecessary();
@ -869,7 +880,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
mp -> genericOnCompletion();
private void genericOnCompletion() {
endPlayback(false);
endPlayback(false, true, true);
}
private final MediaPlayer.OnBufferingUpdateListener audioBufferingUpdateListener =

View File

@ -786,6 +786,11 @@ public class PlaybackService extends MediaBrowserServiceCompat {
if (!(playable instanceof FeedMedia)) {
Log.d(TAG, "Not doing post-playback processing: media not of type FeedMedia");
if (ended) {
playable.onPlaybackCompleted(getApplicationContext());
} else {
playable.onPlaybackPause(getApplicationContext());
}
return;
}
FeedMedia media = (FeedMedia) playable;
@ -1587,7 +1592,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override
public void onStop() {
Log.d(TAG, "onStop()");
mediaPlayer.stop();
mediaPlayer.stopPlayback(true);
}
@Override

View File

@ -8,6 +8,8 @@ import android.util.Log;
import android.util.Pair;
import android.view.SurfaceHolder;
import java.util.concurrent.Future;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.util.playback.Playable;
@ -226,18 +228,43 @@ public abstract class PlaybackServiceMediaPlayer {
protected abstract void setPlayable(Playable playable);
public void skip() {
endPlayback(true);
endPlayback(true, true, true);
}
protected abstract void endPlayback(boolean wasSkipped);
/**
* Ends playback of current media (if any) and moves into INDETERMINATE state, unless
* {@param toStoppedState} is set to true, in which case it moves into STOPPED state.
*
* @see #endPlayback(boolean, boolean, boolean)
*/
public Future<?> stopPlayback(boolean toStoppedState) {
return endPlayback(true, false, toStoppedState);
}
/**
* Moves the PSMP into STOPPED state. This call is only valid if the player is currently in
* INDETERMINATE state, for example after a call to endPlayback.
* This method will only take care of changing the PlayerStatus of this object! Other tasks like
* abandoning audio focus have to be done with other methods.
* Internal method that handles end of playback.
*
* Currently, it has 4 use cases:
* <ul>
* <li>Media playback has completed: call with (false, true, true)</li>
* <li>User asks to skip to next episode: call with (true, true, true)</li>
* <li>Stopping the media player: call with (true, false, true)</li>
* <li>We want to change the media player implementation: call with (true, false, false)</li>
* </ul>
*
* @param wasSkipped If true, we assume the current media's playback has ended, for
* purposes of post playback processing.
* @param shouldContinue If true, the media player should try to load, and possibly play,
* the next item, based on the user preferences and whether such item
* exists.
* @param toStoppedState If true, the playback state gets set to STOPPED if the media player
* is not loading/playing after this call, and the UI will reflect that.
* Only relevant if {@param shouldContinue} is set to false, otherwise
* this method's behavior defaults as if this parameter was true.
*
* @return a Future, just for the purpose of tracking its execution.
*/
public abstract void stop();
protected abstract Future<?> endPlayback(boolean wasSkipped, boolean shouldContinue, boolean toStoppedState);
/**
* @return {@code true} if the WifiLock feature should be used, {@code false} otherwise.

View File

@ -15,6 +15,8 @@ import android.widget.Toast;
import com.google.android.gms.cast.ApplicationMetadata;
import com.google.android.libraries.cast.companionlibrary.cast.BaseCastManager;
import java.util.concurrent.ExecutionException;
import de.danoeh.antennapod.core.cast.CastConsumer;
import de.danoeh.antennapod.core.cast.CastManager;
import de.danoeh.antennapod.core.cast.DefaultCastConsumer;
@ -182,8 +184,11 @@ public class PlaybackServiceFlavorHelper {
boolean wasLaunched) {
PlaybackServiceMediaPlayer mediaPlayer = callback.getMediaPlayer();
if (mediaPlayer != null) {
//TODO change implementation to new protocol
// mediaPlayer.endPlayback(true, true);
try {
mediaPlayer.stopPlayback(false).get();
} catch (InterruptedException | ExecutionException e) {
Log.e(TAG, "There was a problem stopping playback while switching media players", e);
}
mediaPlayer.shutdownQuietly();
}
mediaPlayer = newPlayer;

View File

@ -15,6 +15,8 @@ import com.google.android.libraries.cast.companionlibrary.cast.exceptions.CastEx
import com.google.android.libraries.cast.companionlibrary.cast.exceptions.NoConnectionException;
import com.google.android.libraries.cast.companionlibrary.cast.exceptions.TransientNetworkDisconnectionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicBoolean;
import de.danoeh.antennapod.core.R;
@ -121,7 +123,7 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
}
if (playbackEnded) {
// This is an unconventional thing to occur...
endPlayback(true);
endPlayback(true, true, true);
}
}
@ -259,13 +261,13 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
if (mediaChanged && currentMedia != null) {
media = currentMedia;
}
endPlayback(false);
endPlayback(false, true, true);
return;
case MediaStatus.IDLE_REASON_ERROR:
Log.w(TAG, "Got an error status from the Chromecast. Skipping, if possible, to the next episode...");
callback.onMediaPlayerInfo(CAST_ERROR_PRIORITY_HIGH,
R.string.cast_failed_media_error_skipping);
endPlayback(true);
endPlayback(true, true, true);
return;
}
break;
@ -597,7 +599,7 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
}
@Override
protected void endPlayback(boolean wasSkipped) {
protected Future<?> endPlayback(boolean wasSkipped, boolean shouldContinue, boolean toStoppedState) {
Log.d(TAG, "endPlayback() called");
boolean isPlaying = playerStatus == PlayerStatus.PLAYING;
if (playerStatus != PlayerStatus.INDETERMINATE) {
@ -611,32 +613,43 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
}
}
final Playable currentMedia = media;
Playable nextMedia = callback.getNextInQueue(currentMedia);
Playable nextMedia = null;
if (shouldContinue) {
nextMedia = callback.getNextInQueue(currentMedia);
boolean playNextEpisode = isPlaying && nextMedia != null && UserPreferences.isFollowQueue();
if (playNextEpisode) {
Log.d(TAG, "Playback of next episode will start immediately.");
} else if (nextMedia == null){
Log.d(TAG, "No more episodes available to play");
} else {
Log.d(TAG, "Loading next episode, but not playing automatically.");
boolean playNextEpisode = isPlaying && nextMedia != null && UserPreferences.isFollowQueue();
if (playNextEpisode) {
Log.d(TAG, "Playback of next episode will start immediately.");
} else if (nextMedia == null){
Log.d(TAG, "No more episodes available to play");
} else {
Log.d(TAG, "Loading next episode, but not playing automatically.");
}
if (nextMedia != null) {
callback.onPlaybackEnded(nextMedia.getMediaType(), !playNextEpisode);
// setting media to null signals to playMediaObject() that we're taking care of post-playback processing
media = null;
playMediaObject(nextMedia, false, true /*TODO for now we always stream*/, playNextEpisode, playNextEpisode);
}
}
if (shouldContinue || toStoppedState) {
if (nextMedia != null) {
callback.onPlaybackEnded(null, true);
stop();
}
callback.onPostPlayback(currentMedia, !wasSkipped, nextMedia != null);
} else if (isPlaying) {
callback.onPlaybackPause(currentMedia,
currentMedia != null ? currentMedia.getPosition() : INVALID_TIME);
}
if (nextMedia != null) {
callback.onPlaybackEnded(nextMedia.getMediaType(), !playNextEpisode);
// setting media to null signals to playMediaObject() that we're taking care of post-playback processing
media = null;
playMediaObject(nextMedia, false, true /*TODO for now we always stream*/, playNextEpisode, playNextEpisode);
} else {
callback.onPlaybackEnded(null, true);
stop();
}
callback.onPostPlayback(currentMedia, !wasSkipped, nextMedia != null);
FutureTask<?> future = new FutureTask<>(() -> {}, null);
future.run();
return future;
}
@Override
public void stop() {
private void stop() {
if (playerStatus == PlayerStatus.INDETERMINATE) {
setPlayerStatus(PlayerStatus.STOPPED, null);
} else {