Reputation: 123
I'm working on a Service
that plays a song picked from an ArrayList
.
The problem comes when I use prepare()
and prepareAsync()
methods, so here's the code:
public class MusicService extends Service implements MediaPlayer.OnPreparedListener{
private final static int DEF_VALUE = -1;
private Context mContext;
private ArrayList<Song> mSongsList;
private MediaPlayer mPlayer;
private boolean isPlaying = false;
private int mPosition;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate(){
super.onCreate();
mContext = getApplicationContext();
mSongsList = new ArrayList<Song>();
mPlayer = new MediaPlayer();
setMusicPlayer();
}
public void setMusicPlayer(){
//Setting player properties
mPlayer.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
mPlayer.setAudioSessionId(AudioManager.STREAM_MUSIC);
mPlayer.setOnPreparedListener(this);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
int mCommand = intent.getIntExtra("CMD", DEF_VALUE);
int mTrackSelected = intent.getIntExtra("TRACK_CLICKED", DEF_VALUE);
/** SERVICE COMMANDS
* 0 = SERVICE PERMISSIONS
* 1 = PLAY_PAUSE
* 2 = NEXT TRACK
* 3 = PREV TRACK
* 4 = SONG IN LIST
*/
switch (mCommand){
case 0:
GetTracks mGetSongs = new GetTracks();
mSongsList = mGetSongs.listAllSongs(mContext);
Log.d("test size arrayList", ""+mSongsList.size());
break;
case 1:
setPlayPause();
break;
case 2:
skipNextTrack();
break;
case 3:
skipPrevTrack();
break;
case 4:
if(mTrackSelected != DEF_VALUE){
mPosition = mTrackSelected;
setSongToPlay(mPosition);
}
break;
default:
Log.d("error cmd", "You shouldn't be here!");
break;
}
return START_STICKY;
}
private void skipPrevTrack() {
Log.d("method called:", "skipPrevTrack()");
if(mPosition == 0){
setSongToPlay(mPosition = mSongsList.size()-1);
} else setSongToPlay(--mPosition);
Log.d("test mPosition service", ""+mPosition);
}
private void skipNextTrack() {
Log.d("method called:", "skipNextTrack()");
if(mPosition == mSongsList.size()-1){
setSongToPlay(mPosition = 0);
} else setSongToPlay(++mPosition);
Log.d("test mPosition service", ""+mPosition);
}
private void setSongToPlay(int pos) {
Log.d("method called:", "setSongToPlay("+pos+")");
startSong(mSongsList.get(pos).getSongUri());
}
private void startSong(Uri uri){
try{
mPlayer.reset();
mPlayer.setDataSource(mContext, uri);
mPlayer.prepareAsync();
/** +++WORKS FINE WITH THIS CODE+++
mPlayer = MediaPlayer.create(mContext, uri);
mPlayer.start();
*/
} catch (Exception e){
e.printStackTrace();
}
}
private void setPlayPause() {
Log.d("method called:", "setPlayPause()");
}
@Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
}
LOG:
12-19 16:12:18.325 4013-4013/sebbsoft.myApp D/method called:: setSongToPlay(3)
12-19 16:12:18.341 4013-4013/sebbsoft.myApp D/test song playing: Fiori del male ft. Sfera Ebbasta
12-19 16:12:18.363 4013-4013/sebbsoft.myApp D/MediaPlayer: setSubtitleAnchor in MediaPlayer
12-19 16:12:18.428 4013-4028/sebbsoft.myApp E/MediaPlayer: error (1, -19)
How can I solve it?
Upvotes: 3
Views: 8068
Reputation: 768
I was getting the same problem, I solved it by adding the following code to release the player:
1- get Audio use Assets File
MediaPlayer mediaPlayer = new MediaPlayer();
AssetFileDescriptor afd;
mediaPlayer.setDataSource(afd.getFileDescriptor(),afd.getStartOffset(),afd.getLength());
mediaPlayer.prepare();
mediaPlayer.start();
mp1.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
mp.release();}; });
2- get Audio use res/raw folder.
mp1 = MediaPlayer.create(sound.this, R.raw.pan1);
mp1.start();
mp1.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
mp.release();
}; });
Upvotes: 0
Reputation: 119
I had the same problem. Uploading many diferent audios to a Mediaplayer.
after 15 times it stopped to work.
I just add release every time before upload a new audio in the same Mediaplayer and this worked for me!
no more Error (1, -19)
hope helps!
Upvotes: 1
Reputation: 1110
Finally solved it! After a lot of trial & error!! This is the line that causes it for me:
mMediaPlayer.setAudioSessionId(AudioManager.STREAM_MUSIC);
The API doc for setAudioSessionId doesn't indicate any alternative or any issue on still using it which made it hard to debug. But I saw setAudioStreamType just below it, which is deprecated and suggests setAudioAttributes instead, w/c is added in API 21.
So I changed the above code to this:
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
mMediaPlayer.setAudioAttributes(new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build());
} else {
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
}
And now it works!
By the way, as you can see, I checked only against version N (API 24) and above instead of from LOLLIPOP even though setAudioAttributes
was added in API 21, because I only encounter this issue on Nougat devices, setAudioSessionId
works up to Marshmallow devices for me so... don't wanna break anything that's already working.
I do hope someone would stop by and explain why this happens and what the error really means.
Hope this saves anyone days of headache :)
EDIT:
For anyone Googling around, I also changed the original mMediaPlayer.setAudioSessionId
to mMediaPlayer.setAudioStreamType
, as pointed out by Eugen Pechanec. See his comment and answer for details.
You would also want to change from checking against Build.VERSION_CODES.N
to Build.VERSION_CODES.LOLLIPOP
with that.
Upvotes: 4
Reputation: 38243
You're using setAudioSessionId
incorrectly.
The audio session ID is a system wide unique identifier for the audio stream played by this MediaPlayer instance.
[...]
When created, a MediaPlayer instance automatically generates its own audio session ID. However, it is possible to force this player to be part of an already existing audio session by calling this method. This method must be called before one of the overloaded
setDataSource
methods.
You would obtain an audio session ID by calling AudioManager.generateAudioSessionId()
.
What you're probably looking for if you want to set the stream type is MediaPlayer.setAudioStreamType(int)
.
Newer API for this available since Android 5 is MediaPlayer.setAudioAttributes(AudioAttibutes)
.
Upvotes: 2
Reputation: 13153
Make sure you don't have too many MediaPlayer
objects active at once.
Implement the OnCompletionListener
and register it with your MediaPlayer
instance.
onCompletion
called when the end of a media source is reached during playback.
void onCompletion(MediaPlayer mp){
//Here you stop it.
mp.stop();
mPlayer.reset()
//Reset the data source path to the new file
mp.setDataSource(<uri>);
mPlayer.prepare(); // or mPlayer.prepareAsync();
// start the mediaplayer after the prepare has completed.
}
Release the mediaplayer
instance once you are done playing all the files.
May be your listener only listen one time, then when it try to play the next one without prepared then it can crash.. try this too
add this -- > mPlayer.setOnPreparedListener(this);
in your startSong()
method before your prepareAsync
Upvotes: 0