I have 3 MediaPlayers: Track 0, Track 1, and Track 2. I want each track to start once the one before it finishes, and once that last track finishes, I want to go back to song 0 and start the cycle over. Here is the loop I made that is failing to do this:
public void myMusic() {
while (y > 0) {
music.get(track).start();
if (music.get(track).isPlaying() == false) {
music.get(track).stop();
track++;
if (track == 3) {
track = 0;
for (int i = 0; i < 3; i++) {
try {
music.get(i).prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
music.get(i).seekTo(0);
}
}
}
y--;
}
}
I think you have to close the first if block befor you check if track==3. But this is just a suggestion i dont try it.
Frankly I'd abandon the whole loop concept and go with an OnCompletionListener. Something like this might work -- I haven't tested this, just drew this out of my crazy head, but take it as conceptual:
private class LoopingCompletionListener implements OnCompletionListener {
private int index;
private int count;
private int[] ids;
private Resources res;
public LoopingCompletionListener(int[] musicIds, Resources res) {
//start on the second ID since you start the player with
//the first one
index = 1;
count = musicIds.length();
this.ids = ids;
this.res = res;
}
#Override
public void onCompletion(MediaPlayer mp) {
//Basically, just keep it between 0-2 -- this might need tweaking
index = ((index + 2) % count) - 1;
AssetFileDescriptor afd = res.openRawResourceFd(ids[index]);
FileDescriptor fd = afd.getFileDescriptor();
mp.reset();
mp.setDataSource(fd, afd.getStartOffset(), afd.getLength());
mp.prepare();
mp.start();
}
}
//Meanwhile, in onCreate()...
int[] musicIds = { R.raw.track01, R.raw.track02, R.raw.track03 };
MediaPlayer mediaPlayer = MediaPlayer.create(this, musicIds[0]);
mediaPlayer.setOnCompletionListener(
new LoopingCompletionListener(musicIds, getResources());
mediaPlayer.start();
If I had to guess, isPlaying() is only false when you set the MediaPlayer state to pause or stop. In other words, you can't simply rely on it to determine when the music has reached the end of the stream to determine when it's done.
Also, from the documentation:
Note that the transition from the Started state to the Paused state and vice versa happens asynchronously in the player engine. It may take some time before the state is updated in calls to isPlaying(), and it can be a number of seconds in the case of streamed content.
So it is an unreliable indicator for a use case such as this.
What you could do instead is take advantage of the MediaPlayer.OnCompletionListener. Register one for each MediaPlayer available whereby, upon completion, you advance to the next MediaPlayer.
So something like this:
public class PlayUponCompleteListener implements MediaPlayer.OnCompletionListener {
WeakReference<MediaPlayer> mNextPlayer;
public PlayUponCompleteListener(MediaPlayer mp) {
mNextPlayer = new WeakReference<MediaPlayer>(mp);
}
#Override
public void onCompletion(MediaPlayer mp) {
mp.stop();
mp.reset();
if(mNextPlayer.get() != null) {
mNextPlayer.start();
}
}
}
Then, register them like so:
mp1.setOnCompletionListener(new CompletionListener(mp2));
mp2.setOnCompletionListener(new CompletionListener(mp3));
mp3.setOnCompletionListener(new CompletionListener(mp1));
Related
I’m just starting Android development and Java, and this is my first app to get acquainted with Android development. I’m almost done with the app, the only thing left is shuffling songs. I tried many steps to get it right, and I’ve scoured the web and SO for related question, yet I still can’t get it right.
This snippet of code is in my Service class, Playlist is passed from Main Activity:
public void setPlayList(ArrayList<SongModel> playlist) {
playList = playlist;
//Arraylist of integer to hold the number of indices
list = new ArrayList<Integer>();
for(int i =0; i <= playList.size();i++){
list.add(i);
}
Collections.shuffle(list, new Random(System.nanoTime()));
}
The code below is how songs are played, this snippet is in configPlayBack() method that plays the song from the song id:
long item = 0;
item = playList.get(MusicPref.isShuffle(this)? list.get(position): position).getSongId();
the snippet for playing next song is:
public void playNext() {
position++;
if (position >= playList.size()) {
position = 0;
}
configPlayBack();
}
But the songs are still playing serially.
EDIT:
public void configPlayBack(){
prepared = true;
player.reset();
if ( playList.size()>0){
long item = 0;
item = playList.get(MusicPref.isShuffle(this)? list.get(position):position).getSongId();
playItem(item);
}
}
public void playItem(long item){
Uri base = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Uri trackUri = ContentUris.withAppendedId(base, item);
try{
player.setDataSource(getApplicationContext(), trackUri);
}
catch(Exception e){
Log.e(TAG, "Errror setting data source", e);
}
try{
player.prepare();
}
catch(Exception ee)
{
ee.printStackTrace();
Log.e(TAG, "Error setting data source", ee);
Toast.makeText(getApplicationContext(), "Song corrupt or not supported", Toast.LENGTH_SHORT).show();
ee.printStackTrace();
isReady = false;
}
player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
isReady = true;
setPlayState();
getAudioFocus();
mp.start();
updateNotificationPlayer();
updatePlayback();
updateSeek();
player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
isReady = false;
mp.reset();
playNext();
}
});
}
});
}
Okay you are shuffling your newly created list with
Collections.shuffle(list, new Random(System.nanoTime()));
but you are still using the old playlist
item = playList.get(MusicPref.isShuffle(this)? list.get(position): position).getSongId();
And I am not sure why you are using that line at all. Change it simply to
item = list.get(position).getSongId();
I am getting list of mp3 files(as a ArrayList(Path)) from local by choosing one by one but I could not able to make them to play one by one play if finished move to next and if finished all list loop again. any help?
I solved issue thansk to : http://www.helloandroid.com/tutorials/musicdroid-audio-player-part-i
ofcourse I did a little bit modification, like:
private void playSong() {
try {
mp.reset();
mp.setDataSource(playlist.get(currentPosition));
mp.prepare();
mp.start();
// Setup listener so next song starts automatically
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer arg0) {
nextSong();
}
});
} catch (IOException e) {
Log.v(getString(R.string.app_name), e.getMessage());
}
}
private void nextSong() {
if (++currentPosition >= playlist.size()) {
// Last song, just reset currentPosition
currentPosition = 0;
} else {
// Play next song
playSong(/*MEDIA_PATH + playlist.get(currentPosition)*/);
}
}
I am building an app which plays several videos, and I have two different user scenarios :
Scenario 1. While video 'A' is playing, if user clicks next button, then it stops and play the next video 'B'.
Scenario 2. Play video 'A', and if it's done, user clicks next button and it plays video 'B'.
For the first scenario, I used mediaPlayer.isPlaying() method to detect if it is in Started state and it works fine. However, if I use the same code for the second scenario, isPlaying() throws IllegalStateException.
Here's my code for playing videos :
private void playVideos(SurfaceHolder holder) {
try {
mediaPlayer = new MediaPlayer();
mediaPlayer.setDisplay(holder);
Uri uri = Uri.parse("android.resource://" + getPackageName() + "/" + video_files[mCounter]);
mediaPlayer.setDataSource(this, uri);
mediaPlayer.prepare();
mediaPlayer.start();
mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
if (mCounter <= 8) {
onVideoCompletion(mediaPlayer);
} else {
mediaPlayer.stop();
mediaPlayer.release();
}
}
});
} catch (IOException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalStateException e) {
}
}
Also, here's my button listener to play next video :
nextBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(mediaPlayer != null) {
if(mediaPlayer.isPlaying()) {
mediaPlayer.stop();
mediaPlayer.release();
}
}
mCounter += 1;
if (mCounter <= 8) {
playVideos(holder);
}
}
});
One way that I tried to hack this issue was using a boolean variable instead of isPlaying() method. For example,
boolean mIsPlaying = false;
...
// in button listener
if(mIsPlaying) {
mediaPlayer.stop();
mediaPlayer.release();
}
...
// in playVideos() function
mediaPlayer.start();
mIsPlaying = true;
mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
if (mCounter <= 8) {
onVideoCompletion(mediaPlayer);
} else {
mediaPlayer.stop();
mediaPlayer.release();
}
}
});
That works for my both of scenario, but I'm not sure if it's the correct way to do it. Isn't there any way to detect whether mediaPlayer is in Stopped state?
I took a look at Google's Documentation which you can find here. You can only know if the player isPlaying(); or isLooping(); ... So no, there is not an "easy" or "short" way to achieve what you want. Hope it helped.
I am implementing a game, and I want to use one and only one MediaPlayer instance, running in a separate thread from the UI. Each audio file is sent to a FIFO queue, and played immediately if no file is playing, or as soon as the previous file in the queue is finished.
I have implemented a singleton class to support this as follows:
public class ThePlayer {
private static ThePlayer instance = null;
private Context context;
Thread playerThread;
BlockingQueue<Integer> listIDs = new LinkedBlockingQueue<Integer>();
MediaPlayer player;
Integer playID;
private ThePlayer() {
player = null;
playerThread = new Thread(new Runnable() {
//Object locker = new Object();
public void run() {
while (true) {
try {
playID = listIDs.take(); // blocks if list empty
Log.d(this.getClass().getName(), "Took ID 0x" + Integer.toHexString(playID));
player = MediaPlayer.create(context, playID);
player.setOnCompletionListener(
new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
mp.release();
mp = null;
synchronized (playerThread) {
Log.d(this.getClass().getName(), "Finished playing 0x"+Integer.toHexString(playID));
playerThread.interrupt();
}
}
});
player.start();
Log.d(this.getClass().getName(), "Started playing 0x"+Integer.toHexString(playID));
synchronized (this) {
Log.d(this.getClass().getName(), "Now wait to finish");
wait(); // until finished playing
}
} catch (InterruptedException ie) {
Log.d(getClass().getName(), "Player thread wait interrupted");
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
playerThread.setName("PlayerThread");
playerThread.start();
}
public static ThePlayer getInstance()
{
if(instance == null)
{
instance = new ThePlayer();
}
return instance;
}
public void play(Context _ctx, int id) {
context = _ctx;
listIDs.add(id);
Log.d(this.getClass().getName(), "Added ID 0x" + Integer.toHexString(id));
}
public void stop() {
//TODO: implement
}
}
In general, this works well, but one particular file fails to play in an inconsistent manner. In other words, in most cases, it does not play, but occasionally it does. All the log entries indicate that the file is queued, the MediaPlayer is allocated and started, and that the onCompletion function is called. At no point is an exception thrown.
This happens on a Motorola Defy+ phone and various emulator configurations. It does not happen on my Samsung Galaxy Tab II 10.1, and my guess is that is a function of more capable hardware, or possibly the more advanced version of Android.
I have managed to get a working video player that can stream rtsp links, however im not sure how to display the videos current time position in the UI, i have used the getDuration and getCurrentPosition calls, stored this information in a string and tried to display it in the UI but it doesnt seem to work
**in main.xml:**
TextView android:id="#+id/player"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="1px"
android:text="#string/cpos"
/>
**in strings.xml:**
string name="cpos">"" /string>
**in Player.java**
private void playVideo(String url) {
try {
media.setEnabled(false);
if (player == null) {
player = new MediaPlayer();
player.setScreenOnWhilePlaying(true);
} else {
player.stop();
player.reset();
}
player.setDataSource(url);
player.getCurrentPosition();
player.setDisplay(holder);
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.setOnPreparedListener(this);
player.prepareAsync();
player.setOnBufferingUpdateListener(this);
player.setOnCompletionListener(this);
} catch (Throwable t) {
Log.e(TAG, "Exception in media prep", t);
goBlooey(t);
try {
try {
player.prepare();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.v(TAG, "Duration: ===> " + player.getDuration());
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private Runnable onEverySecond = new Runnable() {
public void run() {
if (lastActionTime > 0
&& SystemClock.elapsedRealtime() - lastActionTime > 3000) {
clearPanels(false);
}
if (player != null) {
timeline.setProgress(player.getCurrentPosition());
//stores getCurrentPosition as a string
cpos = String.valueOf(player.getCurrentPosition());
System.out.print(cpos);
}
if (player != null) {
timeline.setProgress(player.getDuration());
//stores getDuration as a string
cdur = String.valueOf(player.getDuration());
System.out.print(cdur);
}
if (!isPaused) {
surface.postDelayed(onEverySecond, 1000);
}
}
};
Your code snippet looks significantly like my vidtry sample. getCurrentPosition() and getDuration() works for HTTP streaming, such as for use in updating the progress bar.
I have not tried vidtry with an RTSP video stream, mostly because I don't know of any.
Check the SDP response from the server to ensure that it is sending the duration in the response (live streams don't have a recognizable time and that may cause the client to not provide this information.)
E.g. a live feed will look like:
a=range:npt=0-
Whereas a VoD clip should look like:
a=range:npt=0-399.1680
If getCurrentPosition() doesn't work, but you know the Duration (either getDuration() works or you have an alternate way of getting this information; you could calculate it by watching the buffering events and tracking this your self. Your approach is the more desirable approach than this one.
If I got you right, you want to show in a TextView elapsed time e.g. hh:mm:ss?
If so, I'll give you a little walkthrough on how to do that.
private TextView mElapsedTimeText;
private VideoView mVideoView;
private Thread mThread;
#Override
public void onCreate(Bundle savedInstanceState) {
/* here goes your code */
// let's assume that your IDs are elapsedId and videoId
mElapsedTimeText = (TextView) findViewById(R.id.elapsedId);
mVideoView = (VideoView) findViewById(R.id.videoId);
mThread = new Thread() {
#Override
public void run() {
mElapsedTime.setText(getNiceString());
mVideoView.postDelayed(mThread, 1000);
}
}
/* here goes your code */
}
public String getNiceString() {
String result = "";
int position = mVideoView.getCurrentPosition();
/* here goes your code */
//result is hh:mm:ss formatted string
return result;
}
#Override
public void onPrepared(MediaPlayer mp) {
/* here goes your code */
// you have to trigger the process somewhere
mVideoView.postDelayed(mThread, 1000);
/* here goes your code */
}
And one more thing I forgot to mention. In order to make this work your activity class has to implement the OnPreparedListener interface.
I hope you or someone else will find this post useful.
Best regards,
Igor