Skip to content

Commit

Permalink
Possible fix for race condition of issue #48 in GaplessPlayer
Browse files Browse the repository at this point in the history
  • Loading branch information
djselbeck committed Feb 20, 2017
1 parent 536bbbb commit 633dd54
Showing 1 changed file with 114 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ public synchronized void play(String uri, int jumpTime) throws PlaybackException
* the playback
*/
public synchronized void togglePause() {
// Check if a Mediaplayer exits and if it is actual plaiyng
// Check if a Mediaplayer exits and if it is actual playing
if (mCurrentMediaPlayer != null && mCurrentMediaPlayer.isPlaying()) {
// In this case pause the playback
mCurrentMediaPlayer.pause();
Expand All @@ -214,7 +214,7 @@ public synchronized void togglePause() {
* Just pauses currently running player
*/
public synchronized void pause() {
// Check if a Mediaplayer exits and if it is actual plaiyng
// Check if a MediaPlayer exits and if it is actual playing
if (mCurrentMediaPlayer != null && mCurrentMediaPlayer.isPlaying()) {
mCurrentMediaPlayer.pause();
}
Expand All @@ -231,7 +231,7 @@ public synchronized void resume() {
}

/**
* Stops mediaplayback
* Stops media playback
*/
public synchronized void stop() {
// Check if a player exists otherwise there is nothing to do.
Expand Down Expand Up @@ -373,7 +373,7 @@ public synchronized void setNextTrack(String uri) throws PlaybackException {
if (mNextMediaPlayer != null) {
// Remove this player from the currently active one as a next one
mCurrentMediaPlayer.setNextMediaPlayer(null);
// Release the player that is not needed anylonger
// Release the player that is not needed any longer
mNextMediaPlayer.release();

// Reset internal state variables
Expand Down Expand Up @@ -429,67 +429,80 @@ public synchronized void setNextTrack(String uri) throws PlaybackException {

@Override
public void onPrepared(MediaPlayer mp) {
// If mp equals currentMediaPlayback it should start playing
mCurrentPrepared = true;
// Sequentially execute all critical operations on the MP objects
synchronized (GaplessPlayer.this) {
// Check if the callback happened from the current media player
if (!mp.equals(mCurrentMediaPlayer)) {
return;
}
// If mp equals currentMediaPlayback it should start playing
mCurrentPrepared = true;

// only start playing if its desired
// only start playing if its desired

// Check if an immediate jump is requested
if (mPrepareTime > 0) {
mp.seekTo(mPrepareTime);
mPrepareTime = 0;
}
mp.setWakeMode(mPlaybackService.getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
mp.start();

/*
* Signal audio effect desire to android
*/
Intent audioEffectIntent = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
audioEffectIntent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, mp.getAudioSessionId());
audioEffectIntent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, mPlaybackService.getPackageName());
mPlaybackService.sendBroadcast(audioEffectIntent);
mp.setAuxEffectSendLevel(1.0f);
Log.v(TAG, "Opened audio effects for first player");

// Notify connected listeners
for (OnTrackStartedListener listener : mTrackStartListeners) {
listener.onTrackStarted(mPrimarySource);
}
// Check if an immediate jump is requested
if (mPrepareTime > 0) {
mp.seekTo(mPrepareTime);
mPrepareTime = 0;
}
mp.setWakeMode(mPlaybackService.getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
mp.start();

try {
mSecondPreparingLock.acquire();
} catch (InterruptedException e) {
// FIXME some handling? Not sure if necessary
}
/*
* Signal audio effect desire to android
*/
Intent audioEffectIntent = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
audioEffectIntent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, mp.getAudioSessionId());
audioEffectIntent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, mPlaybackService.getPackageName());
mPlaybackService.sendBroadcast(audioEffectIntent);
mp.setAuxEffectSendLevel(1.0f);

// If second MediaPlayer exists and is not already prepared and not already preparing
// Starte preparing the second MP here.
if (mSecondPrepared == false && mNextMediaPlayer != null && !mSecondPreparing) {
mSecondPreparing = true;
// Delayed initialization second mediaplayer
mNextMediaPlayer.prepareAsync();
// Notify connected listeners
for (OnTrackStartedListener listener : mTrackStartListeners) {
listener.onTrackStarted(mPrimarySource);
}

try {
mSecondPreparingLock.acquire();
} catch (InterruptedException e) {
// FIXME some handling? Not sure if necessary
}

// If second MediaPlayer exists and is not already prepared and not already preparing
// Start preparing the second MP here.
if (!mSecondPrepared && mNextMediaPlayer != null && !mSecondPreparing) {
mSecondPreparing = true;
// Delayed initialization second mediaplayer
mNextMediaPlayer.prepareAsync();
}
mSecondPreparingLock.release();
}
mSecondPreparingLock.release();
}
};

private OnPreparedListener mSecondaryPreparedListener = new MediaPlayer.OnPreparedListener() {

@Override
public void onPrepared(MediaPlayer mp) {
// Second MediaPlayer is now ready to be used and can be set as a next MediaPlayer to the current one
mSecondPreparing = false;

// If it is nextMediaPlayer it should be set for currentMP
mp.setWakeMode(mPlaybackService.getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
// Sequentially execute all critical operations on the MP objects
synchronized (GaplessPlayer.this) {
// Check if the callback happened from the current second media player, it can
// happen that callbacks are called when the MP is no longer relevant, abort then.
if(!mp.equals(mNextMediaPlayer)) {
return;
}
// Second MediaPlayer is now ready to be used and can be set as a next MediaPlayer to the current one
mSecondPreparing = false;

// Set the internal state
mSecondPrepared = true;
// If it is nextMediaPlayer it should be set for currentMP
mp.setWakeMode(mPlaybackService.getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);

// Set this now prepared MediaPlayer as the next one
mCurrentMediaPlayer.setNextMediaPlayer(mp);
// Set the internal state
mSecondPrepared = true;

// Set this now prepared MediaPlayer as the next one
mCurrentMediaPlayer.setNextMediaPlayer(mp);
}
}
};

Expand Down Expand Up @@ -548,54 +561,61 @@ private class TrackCompletionListener implements MediaPlayer.OnCompletionListene

@Override
public void onCompletion(MediaPlayer mp) {
// Get audio session id to close the audioFX session
int audioSessionID = mp.getAudioSessionId();

// Reset the current MediaPlayer variable
mCurrentMediaPlayer = null;
/*
* Signal android desire to close audio effect session
*/
Intent audioEffectIntent = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
audioEffectIntent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, audioSessionID);
audioEffectIntent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, mPlaybackService.getPackageName());
mPlaybackService.sendBroadcast(audioEffectIntent);

// Notify connected listeners that the last track is now finished
for (OnTrackFinishedListener listener : mTrackFinishedListeners) {
listener.onTrackFinished();
}

// Set current MP to next MP if one is ready
if (mNextMediaPlayer != null && mSecondPrepared) {
// Next media player should now be playing already, so make this the current one.
mCurrentMediaPlayer = mNextMediaPlayer;
// Register this listener to the now playing MediaPlayer also
mCurrentMediaPlayer.setOnCompletionListener(new TrackCompletionListener());

// Move the second to primary source (URI)
mPrimarySource = mSecondarySource;

// Reset the now obsolete second MediaPlayer state variables
mSecondarySource = null;
mNextMediaPlayer = null;
// Sequentially execute all critical operations on the MP objects
synchronized (GaplessPlayer.this) {
// Check if the callback happened from the current media player
if(!mp.equals(mCurrentMediaPlayer)) {
return;
}
// Get audio session id to close the audioFX session
int audioSessionID = mp.getAudioSessionId();

// Reset the current MediaPlayer variable
mCurrentMediaPlayer = null;
/*
* Signal audio effect desire to android
*/
Intent audioEffectOpenIntent = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
audioEffectOpenIntent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, mp.getAudioSessionId());
audioEffectOpenIntent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, mPlaybackService.getPackageName());
mPlaybackService.sendBroadcast(audioEffectOpenIntent);
mp.setAuxEffectSendLevel(1.0f);
* Signal android desire to close audio effect session
*/
Intent audioEffectIntent = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
audioEffectIntent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, audioSessionID);
audioEffectIntent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, mPlaybackService.getPackageName());
mPlaybackService.sendBroadcast(audioEffectIntent);

// Notify connected listeners that playback has started
for (OnTrackStartedListener listener : mTrackStartListeners) {
listener.onTrackStarted(mPrimarySource);
// Notify connected listeners that the last track is now finished
for (OnTrackFinishedListener listener : mTrackFinishedListeners) {
listener.onTrackFinished();
}
}

mp.release();
// Set current MP to next MP if one is ready
if (mNextMediaPlayer != null && mSecondPrepared) {
// Next media player should now be playing already, so make this the current one.
mCurrentMediaPlayer = mNextMediaPlayer;
// Register this listener to the now playing MediaPlayer also
mCurrentMediaPlayer.setOnCompletionListener(new TrackCompletionListener());

// Move the second to primary source (URI)
mPrimarySource = mSecondarySource;

// Reset the now obsolete second MediaPlayer state variables
mSecondarySource = null;
mNextMediaPlayer = null;

/*
* Signal audio effect desire to android
*/
Intent audioEffectOpenIntent = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
audioEffectOpenIntent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, mp.getAudioSessionId());
audioEffectOpenIntent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, mPlaybackService.getPackageName());
mPlaybackService.sendBroadcast(audioEffectOpenIntent);
mCurrentMediaPlayer.setAuxEffectSendLevel(1.0f);

// Notify connected listeners that playback has started
for (OnTrackStartedListener listener : mTrackStartListeners) {
listener.onTrackStarted(mPrimarySource);
}
}

mp.release();
}
}
}

Expand All @@ -617,7 +637,7 @@ public REASON getReason() {
}

/**
* Returns whether Gaplessplayer is active or inactive so it can receive commands
* Returns whether {@link GaplessPlayer} is active or inactive so it can receive commands
*
* @return True if this class is busy and false if it is not doing important work.
*/
Expand Down

0 comments on commit 633dd54

Please sign in to comment.