summaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
authorDomingos Lopes <domingos86lopes+github@gmail.com>2016-03-26 17:12:50 -0400
committerDomingos Lopes <domingos86lopes+github@gmail.com>2016-04-23 21:39:55 -0400
commitc4b6f366ca2b8e731f87f0d4423c7e02185289f8 (patch)
tree051efb291ce621e48e0476415f38a6560dd3c5e5 /core/src
parent1ca0c1214f849bb2759ca78195f40bb0ee6f6eb8 (diff)
downloadAntennaPod-c4b6f366ca2b8e731f87f0d4423c7e02185289f8.zip
implement several remote playback commands
Diffstat (limited to 'core/src')
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java9
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java232
3 files changed, 130 insertions, 115 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java
index 379875478..684c54d7f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java
@@ -19,7 +19,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
-import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.RewindAfterPauseUtils;
@@ -425,14 +424,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
}
/**
- * Seek to the start of the specified chapter.
- */
- @Override
- public void seekToChapter(@NonNull Chapter c) {
- seekTo((int) c.getStart());
- }
-
- /**
* Returns the duration of the current media object or INVALID_TIME if the duration could not be retrieved.
*/
@Override
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java
index 7570e7db0..13eedfba2 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java
@@ -131,7 +131,9 @@ public abstract class PlaybackServiceMediaPlayer {
/**
* Seek to the start of the specified chapter.
*/
- public abstract void seekToChapter(@NonNull Chapter c);
+ public void seekToChapter(@NonNull Chapter c) {
+ seekTo((int) c.getStart());
+ }
/**
* Returns the duration of the current media object or INVALID_TIME if the duration could not be retrieved.
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java
index 4ae777e65..81da8cf3d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java
@@ -17,15 +17,15 @@ 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.core.cast.CastConsumer;
import de.danoeh.antennapod.core.cast.CastConsumerImpl;
import de.danoeh.antennapod.core.cast.CastManager;
-import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.CastUtils;
+import de.danoeh.antennapod.core.util.RewindAfterPauseUtils;
import de.danoeh.antennapod.core.util.playback.Playable;
/**
@@ -49,7 +49,7 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
* 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 ReentrantLock playerLock;
+ //private final ReentrantLock playerLock;
private final ThreadPoolExecutor executor;
public RemotePSMP(@NonNull Context context, @NonNull PSMPCallback callback) {
@@ -61,7 +61,7 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
mediaType = null;
this.startWhenPrepared = new AtomicBoolean(false);
- playerLock = new ReentrantLock();
+ //playerLock = new ReentrantLock();
executor = new ThreadPoolExecutor(1, 1, 5, TimeUnit.MINUTES, new LinkedBlockingDeque<>(),
(r, executor) -> Log.d(TAG, "Rejected execution of runnable"));
@@ -124,8 +124,16 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
public void onMediaLoadResult(int statusCode) {
if (playerStatus == PlayerStatus.PREPARING) {
if (statusCode == CastStatusCodes.SUCCESS) {
- executor.execute(RemotePSMP.this::onPrepared);
- } else {
+ setPlayerStatus(PlayerStatus.PREPARED, media);
+ if (media.getDuration() == 0) {
+ Log.d(TAG, "Setting duration of media");
+ try {
+ media.setDuration((int) castMgr.getMediaDuration());
+ } catch (TransientNetworkDisconnectionException | NoConnectionException e) {
+ Log.e(TAG, "Unable to get remote media's duration");
+ }
+ }
+ } else if (statusCode != CastStatusCodes.REPLACED){
Log.d(TAG, "Remote media failed to load");
setPlayerStatus(PlayerStatus.INITIALIZED, media);
}
@@ -144,27 +152,19 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
@Override
public void playMediaObject(@NonNull final Playable playable, final boolean stream, final boolean startWhenPrepared, final boolean prepareImmediately) {
Log.d(TAG, "playMediaObject() called");
- executor.execute(() -> {
- playerLock.lock();
- try {
- playMediaObject(playable, false, stream, startWhenPrepared, prepareImmediately);
- } finally {
- playerLock.unlock();
- }
- });
+ playMediaObject(playable, false, stream, startWhenPrepared, prepareImmediately);
}
/**
* Internal implementation of playMediaObject. This method has an additional parameter that allows the caller to force a media player reset even if
* the given playable parameter is the same object as the currently playing media.
- * <p/>
- * This method requires the playerLock and is executed on the caller's thread.
*
* @see #playMediaObject(de.danoeh.antennapod.core.util.playback.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 (!CastUtils.isCastable(playable)) {
+ Log.d(TAG, "media provided is not compatible with cast device");
+ return;
}
if (media != null) {
@@ -193,53 +193,71 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
}
}
- if (CastUtils.isCastable(playable)) {
- this.media = playable;
- remoteMedia = CastUtils.convertFromFeedMedia((FeedMedia) media);
- //this.stream = stream;
- this.mediaType = media.getMediaType();
- this.startWhenPrepared.set(startWhenPrepared);
- setPlayerStatus(PlayerStatus.INITIALIZING, media);
- try {
- media.loadMetadata();
- executor.execute(() -> updateMediaSessionMetadata(media));
- setPlayerStatus(PlayerStatus.INITIALIZED, media);
- if (prepareImmediately) {
- prepareSync();
- }
- } catch (Playable.PlayableException e) {
- Log.e(TAG, "Error while loading media metadata", e);
- setPlayerStatus(PlayerStatus.STOPPED, null);
+ this.media = playable;
+ remoteMedia = CastUtils.convertFromFeedMedia((FeedMedia) media);
+ //this.stream = stream;
+ this.mediaType = media.getMediaType();
+ this.startWhenPrepared.set(startWhenPrepared);
+ setPlayerStatus(PlayerStatus.INITIALIZING, media);
+ try {
+ media.loadMetadata();
+ executor.execute(() -> callback.updateMediaSessionMetadata(media));
+ setPlayerStatus(PlayerStatus.INITIALIZED, media);
+ if (prepareImmediately) {
+ prepare();
}
+ } catch (Playable.PlayableException e) {
+ Log.e(TAG, "Error while loading media metadata", e);
+ setPlayerStatus(PlayerStatus.STOPPED, null);
}
}
@Override
public void resume() {
- //TODO
+ try {
+ setVolume(UserPreferences.getLeftVolume(), UserPreferences.getRightVolume());
+ if (playerStatus == PlayerStatus.PREPARED && media.getPosition() > 0) {
+ int newPosition = RewindAfterPauseUtils.calculatePositionWithRewind(
+ media.getPosition(),
+ media.getLastPlayedTime());
+ castMgr.play(newPosition);
+ }
+ castMgr.play();
+ } catch (CastException | TransientNetworkDisconnectionException | NoConnectionException e) {
+ Log.e(TAG, "Unable to resume remote playback", e);
+ }
}
@Override
public void pause(boolean abandonFocus, boolean reinit) {
- //TODO
+ boolean playing = true;
+ try {
+ playing = castMgr.isRemoteMediaPlaying();
+ if (playing) {
+ castMgr.pause();
+ }
+ } catch (CastException | TransientNetworkDisconnectionException | NoConnectionException e) {
+ Log.e(TAG, "Unable to pause", e);
+ }
+ if (playing && reinit) {
+ reinit();
+ }
}
@Override
public void prepare() {
- executor.submit( () -> {
- playerLock.lock();
- prepareSync();
- playerLock.unlock();
-
- });
- }
-
- private void prepareSync() {
if (playerStatus == PlayerStatus.INITIALIZED) {
Log.d(TAG, "Preparing media player");
setPlayerStatus(PlayerStatus.PREPARING, media);
try {
- castMgr.loadMedia(remoteMedia, startWhenPrepared.get(), media.getPosition());
+ int position = media.getPosition();
+ if (position > 0) {
+ position = RewindAfterPauseUtils.calculatePositionWithRewind(
+ position,
+ media.getLastPlayedTime());
+ }
+ setVolume(UserPreferences.getLeftVolume(), UserPreferences.getRightVolume());
+ castMgr.loadMedia(remoteMedia, startWhenPrepared.get(), position);
} catch (TransientNetworkDisconnectionException | NoConnectionException e) {
Log.e(TAG, "Error loading media", e);
setPlayerStatus(PlayerStatus.INITIALIZED, media);
@@ -247,66 +265,68 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
}
}
- /**
- * Called after media player has been prepared. This method is executed on the caller's thread.
- */
- void onPrepared() {
- playerLock.lock();
-
- if (playerStatus != PlayerStatus.PREPARING) {
- playerLock.unlock();
- Log.w(TAG, "onPrepared() called, but player is not in PREPARING state anymore");
- return;
- }
- Log.d(TAG, "Resource loaded");
- if (media.getDuration() == 0) {
- Log.d(TAG, "Setting duration of media");
- try {
- media.setDuration((int) castMgr.getMediaDuration());
- } catch (TransientNetworkDisconnectionException | NoConnectionException e) {
- Log.e(TAG, "Unable to get remote media's duration");
- }
- }
- setPlayerStatus(PlayerStatus.PREPARED, media);
- playerLock.unlock();
- }
-
@Override
public void reinit() {
- //TODO
+ if (media != null) {
+ playMediaObject(media, true, false, startWhenPrepared.get(), false);
+ } else {
+ Log.d(TAG, "Call to reinit was ignored: media was null");
+ }
}
@Override
public void seekTo(int t) {
- //TODO
+ //TODO check other seek implementations and see if there's no issue with sending too many seek commands to the remote media player
+ try {
+ if (castMgr.isRemoteMediaLoaded()) {
+ setPlayerStatus(PlayerStatus.SEEKING, media);
+ castMgr.seek(t);
+ } else if (media != null && playerStatus == PlayerStatus.INITIALIZED){
+ media.setPosition(t);
+ startWhenPrepared.set(false);
+ prepare();
+ }
+ } catch (TransientNetworkDisconnectionException | NoConnectionException e) {
+ Log.e(TAG, "Unable to seek", e);
+ }
}
@Override
public void seekDelta(int d) {
- //TODO
- }
-
- @Override
- public void seekToChapter(@NonNull Chapter c) {
- //TODO
+ int position = getPosition();
+ if (position != INVALID_TIME) {
+ seekTo(position + d);
+ } else {
+ Log.e(TAG, "getPosition() returned INVALID_TIME in seekDelta");
+ }
}
@Override
public int getDuration() {
- //TODO
- return 0;
- }
-
- @Override
- public int getPosition() {
+ int retVal = INVALID_TIME;
+ boolean prepared;
try {
- if (!playerLock.tryLock(50, TimeUnit.MILLISECONDS)) {
- return INVALID_TIME;
+ prepared = castMgr.isRemoteMediaLoaded();
+ } catch (TransientNetworkDisconnectionException | NoConnectionException e) {
+ Log.e(TAG, "Unable to check if remote media is loaded", e);
+ prepared = playerStatus.isAtLeast(PlayerStatus.PREPARED);
+ }
+ if (prepared) {
+ try {
+ retVal = (int) castMgr.getMediaDuration();
+ } catch (TransientNetworkDisconnectionException | NoConnectionException e) {
+ Log.e(TAG, "Unable to determine remote media's duration", e);
}
- } catch (InterruptedException e) {
- return INVALID_TIME;
}
+ if(retVal == INVALID_TIME && media != null && media.getDuration() > 0) {
+ retVal = media.getDuration();
+ }
+ Log.d(TAG, "getDuration() -> " + retVal);
+ return retVal;
+ }
+ @Override
+ public int getPosition() {
int retVal = INVALID_TIME;
boolean prepared;
try {
@@ -322,24 +342,21 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
Log.e(TAG, "Unable to determine remote media's position", e);
}
}
- if(retVal <= 0 && media != null && media.getPosition() > 0) {
+ if(retVal <= 0 && media != null && media.getPosition() >= 0) {
retVal = media.getPosition();
}
-
- playerLock.unlock();
Log.d(TAG, "getPosition() -> " + retVal);
return retVal;
}
@Override
public boolean isStartWhenPrepared() {
- //TODO
- return false;
+ return startWhenPrepared.get();
}
@Override
public void setStartWhenPrepared(boolean startWhenPrepared) {
- //TODO
+ this.startWhenPrepared.set(startWhenPrepared);
}
//TODO I believe some parts of the code make the same decision skipping this check, so that
@@ -389,8 +406,7 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
@Override
public MediaType getCurrentMediaType() {
- //TODO
- return null;
+ return mediaType;
}
@Override
@@ -402,28 +418,26 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
public void shutdown() {
castMgr.removeCastConsumer(castConsumer);
executor.shutdown();
- //TODO
}
@Override
public void shutdownAsync() {
- //TODO
- this.shutdown();
+ executor.execute(this::shutdown);
+ executor.shutdown();
}
@Override
public void setVideoSurface(SurfaceHolder surface) {
- //TODO
+ throw new UnsupportedOperationException("Setting Video Surface unsupported in Remote Media Player");
}
@Override
public void resetVideoSurface() {
- //TODO
+ throw new UnsupportedOperationException("Resetting Video Surface unsupported in Remote Media Player");
}
@Override
public Pair<Integer, Integer> getVideoSize() {
- //TODO
return null;
}
@@ -443,12 +457,20 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
@Override
public void endPlayback(boolean wasSkipped, boolean switchingPlayers) {
- //TODO
+ boolean isPlaying = playerStatus == PlayerStatus.PLAYING;
+ if (playerStatus != PlayerStatus.INDETERMINATE) {
+ setPlayerStatus(PlayerStatus.INDETERMINATE, media);
+ }
+ callback.endPlayback(isPlaying, wasSkipped, switchingPlayers);
}
@Override
public void stop() {
- //TODO
+ if (playerStatus == PlayerStatus.INDETERMINATE) {
+ setPlayerStatus(PlayerStatus.STOPPED, null);
+ } else {
+ Log.d(TAG, "Ignored call to stop: Current player state is: " + playerStatus);
+ }
}
@Override