im building a app to stream live radio over wifi. The problem im having is how to i keep the app playing when the user minimizes the app?
private void initializeMediaPlayer() {
player = new MediaPlayer();
try {
player.setDataSource("url");
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
player.setOnBufferingUpdateListener(new OnBufferingUpdateListener() {
public void onBufferingUpdate(MediaPlayer mp, int percent) {
// playSeekBar.setSecondaryProgress(percent);
Log.i("Buffering", "" + percent);
}
});
}
You need to look into Services. They will let you do things in the background when the app minimizes.
When your app minimizes, you make an Intent that describes to your Service what is to be streamed and from what time. The Service will then construct a MediaPlayer and continue streaming. Also, a good idea would be to have a Notification that will let the user end this Service without opening the app.
One more condition is when the user maximizes the app after the Service has been playing for some time. In this case, you need a persistence mechanism to keep track. Like SharedPreferences or SQLite.
Documentation: http://developer.android.com/guide/components/services.html
You have to implement your mediaplayer as a service as an Activity will be in onstop state when minimized.
public class MyMediaPlayerService extends Service implements MediaPlayer.OnPreparedListener,MediaPlayer.OnCompletionListener,AudioManager.OnAudioFocusChangeListener {
static MediaPlayer mPlayer = null;
public MyMediaPlayerService() {
}
public int onStartCommand(Intent intent, int flags, int startId) {
if (mPlayer != null && mPlayer.isPlaying()) {
mPlayer.stop();
mPlayer.release();
mPlayer = null;
mPlayer = new MediaPlayer();
}
mPlayer = new MediaPlayer();
try {
mPlayer.setDataSource(intent.getStringExtra("streamurl"));
mPlayer.setLooping(true);
} catch (IOException e) {
e.printStackTrace();
}
mPlayer.setOnPreparedListener(this);
// Request audio focus for playback
mPlayer.prepareAsync();
mPlayer.setLooping(false);
mPlayer.setOnCompletionListener(this);
notification();
return START_NOT_STICKY;
}
Live radio streaming, it's long running process irrespective of UI, so you need to implement complete media player logic in service and your service will be take all the charge for streaming. As per as ui is concerned whatever UI component you are using to represent the streamed data can communicate with service you will implement.
And regarding start and stop streaming, it will be depend on your application design.
I think this approach will be open for all expected application design.
Let me know if you need any help.
Related
I am currently working with FFmpegMediaPlayer in order to make a basic music player given a url. I got the basic functionality to work. On error, I show an error message, else I play the songs.
The problem I am facing is, once the player starts, and I turn off my wifi and phone data, it stops the sound. Once I turn my wifi or phone data back one, I would like the player to continue but it simply stops. I have to stop and play again for it to continue.
Is there a way to have the FFmpegMediaPlayer continue streaming?
This is what I have to initialize the player:
mMediaPlayer = new FFmpegMediaPlayer();
mMediaPlayer.setOnErrorListener(this);
mMediaPlayer.setOnPreparedListener(this);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setOnBufferingUpdateListener(this);
mMediaPlayer.setOnSeekCompleteListener(this);
try {
Uri uri = Uri.parse(radio_url);
mMediaPlayer.setDataSource(mMainActivity, uri);
mMediaPlayer.prepareAsync();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Then I have a function to handle when Play/Stop button is clicked:
#Override
public void onPlayButtonClicked(Button button) {
if (radioInitialized) {
mAnalytics.onStopButtonClicked();
mMainLayout.loading(false);
mMediaPlayer.pause();
button.setBackground(mMainActivity.getResources().getDrawable(R.drawable.play_button));
radioInitialized = false;
} else {
mAnalytics.onPlayButtonClicked();
mMainLayout.loading(true);
initRadio();
button.setBackground(mMainActivity.getResources().getDrawable(R.drawable.stop_button));
radioInitialized = true;
}
}
Again, the basic functionality works, but I would like for it to continue streaming after wifi is turned back on.
Found a solution.
My main goal was to create a media player like a radio.
I tried MediaPlayer and it works but it tends to stop frequently.
I then tried vitamio and FFmpegMediaPlayer. Vitamio was confusing and FFmpegMediaPlayer doesn't support on streaming error.
I currently got it working with Google's Exo Player. There is a very nice tutorial here: https://codelabs.developers.google.com/codelabs/exoplayer-intro/index.html?index=..%2F..%2Findex#0
for last three weeks I have worked on a Media Player in Android.I am trying to find a solution of how can I make my Media Player to change the song when it's already playing one.
Here is my Listener on the RecyclerView
musicList.addOnItemTouchListener(
new RecyclerItemClickListener(getApplicationContext(), new RecyclerItemClickListener.OnItemClickListener() {
#Override
public void onItemClick(View view, final int position) {
currentPosition = position;
if(!mediaPlayer.isPlaying()){
musicThread.start();
} else {
mediaPlayer.reset();
}
}
})
);
}
and my Thread is this:
final Thread musicThread = new Thread(new Runnable(){
#Override
public void run() {
try {
URL = getMusicURL(myDataset[currentPosition]);
try {
mediaPlayer.setDataSource(URL);
//mediaPlayer.prepare(); // might take long! (for buffering, etc)
mediaPlayer.prepareAsync(); // prepare async to not block main thread
} catch (IOException e) {
e.printStackTrace();
Log.i("TEST","Eroare: "+e.getMessage());
}
} catch (StorageApiException e) {
e.printStackTrace();
Log.i("TEST","Eroare: "+e.getMessage());
}
}
});
I think you have a mess. First of all, you dont need a thread to play music, the own mediaplayer API does it for you when you call mediaPlayer.start(). However, you have to care about the time it takes to prepare the data source if you are for example streaming online music. For this, just use mediaPlayer.prepareAsync() and register a callback. When it has finished preparing, you can automatically start playing or do whatever you want.
If you want to change the data source, just follow the automaton map that you can find in MediaPlayer docs. Essentially, when the user selects another track, you register the call in your button listener, then reset the mediaPlayer, and recall all prepare, start... cycle again. By the way, it is advised to deploy all your mediaplayer code into a service so that it can keep playing even though the user has closed your activity.
I have a problem with the built-in media controls, which appear on the lock screen when music is playing through the mediaplayer.
When the music is playing the controls are always present, but when i press the pause button on the lock screen and pause the mediaplayer through mediaplayer.pause() and then turn off the screen and turn it on again (to see the lock screen again), the media controls have vanished, although the player is only paused and not stopped.
With the built-in music player app, which also uses the mediaplayer, this does not happen.
Here are the relevant code sections:
if (action.equals(Intent.ACTION_SCREEN_OFF)) {
if(userPresent)
int result = mAudioManager.requestAudioFocus(mAudioFocusListener,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
configMediaPlayer();
}
userPresent = false;
}
screenOn = false;
}
else if (action.equals(Intent.ACTION_USER_PRESENT)) {
stopMediaPlayer();
userPresent = true;
}
The code is inside a broadcastreceiver. The music shall only be played when the screen is off (a partial wakelock is held) or the lock screen is present.
private void configMediaPlayer(){
if (mediaPlayer == null) {
mediaPlayer = new MediaPlayer();
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
mediaPlayer.setOnPreparedListener(this);
}
else
mediaPlayer.reset();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
mediaPlayer.setDataSource(getApplicationContext(), Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.song));
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
mediaPlayer.prepareAsync();
}
#Override
public void onPrepared(MediaPlayer mp) {
startMediaPlayer();
}
private void startMediaPlayer(){
mediaPlayer.setLooping(true);
mediaPlayer.start();
}
private void stopMediaPlayer(){
mediaPlayer.reset();
mediaPlayer.release();
mediaPlayer = null;
}
The function for the pause event:
public void pause(){
if(isPaused && !userPresent){
startMediaPlayer();
Intent i = new Intent("com.android.music.playstatechanged");
i.putExtra("position", Long.valueOf(123));
i.putExtra("playing", true);
sendStickyBroadcast(i);
isPaused = false;
}
else if (!isPaused && !userPresent){
mediaPlayer.pause();
isPaused = true;
Intent i = new Intent("com.android.music.playstatechanged");
i.putExtra("position", Long.valueOf(123));
i.putExtra("playing", false);
sendStickyBroadcast(i);
}
}
I receive the pause event, when the button is pressed, through a mediabuttonintentreceiver and then execute the pause() function. Because i'm developing the app for gingerbread, the remotecontrolclient isn't available and the only relevant stuff to flag a playsate change in the music player app i found was:
Intent i = new Intent(what);
i.putExtra("id", Long.valueOf(getAudioId()));
i.putExtra("artist", getArtistName());
i.putExtra("album",getAlbumName());
i.putExtra("track", getTrackName());
i.putExtra("playing", isPlaying());
sendStickyBroadcast(i);
The audiofocus isn't lost when i pause the playback, because i receive no messages in the log from the onaudiochangelistener:
private OnAudioFocusChangeListener mAudioFocusListener = new OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
String LOGTAG = "AUDIOFOCUSCHANGELISTENER";
switch (focusChange) {
case AudioManager.AUDIOFOCUS_LOSS:
Log.e(LOGTAG, "AudioFocus: received AUDIOFOCUS_LOSS, turning FM off");
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
Log.e(LOGTAG, "AudioFocus: received AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK");
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
Log.e(LOGTAG, "AudioFocus: received AUDIOFOCUS_LOSS_TRANSIENT");
break;
case AudioManager.AUDIOFOCUS_GAIN:
Log.e(LOGTAG, "AudioFocus: received AUDIOFOCUS_GAIN");
break;
default:
Log.e(LOGTAG, "Unknown audio focus change code " + focusChange);
}
}
};
What am i missing or isn't functioning properly?
I hope that someone can help me, because i've searched the forum, googled and have tested things for days now without success
EDIT: What i forget to mention, the functions/the code is running in a background service. Maybe it is helpfull.
EDIT
Ok, i've finally narrowed it down to a more specific point. This happens only if i start the mediaplayer in conjunction with the Intent.ACTION_SCREEN_OFF.
If i start the mediaplayer before that intent, for example in the onCreate() function of the service and then turn off the screen, turn it back on (lock screen), press the pause button, turn it off again and finally turn it on again, the media controls are visible and in pause state on the lock screen.
But if i start the mediaplayer after receiving the Intent.ACTION_SCREEN_OFF, then the above problem happens.
Can it have something to do with that, that i start the mediaplayer inside the braodcastreceiver-class, which is inside the service, when the problem happens? Or should you simply not start a mediaplayer after the screen has turned off although you have a wake lock?
I have a list of songs that I'm streaming using the MediaPlayer. Some of the songs consistently work and others consistently do not work. I can't see a difference between these files, and they seem to play fine in itunes and such.
When the songs fail it is throwing an IllegalStateException on the mediaPlayer.prepare() line. The IllegalStateException that is thrown has no useful info in it, (detailMessage is null, stackState is null)
Here is my code
try {
mediaPlayer.setDataSource(media.url);
setPlayerState(PlayerState.PREPARING);
mediaPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "bad stream");
}
Here is a url to the file that does NOT work:
skdy.bryceb.dev.mediarain.com/song.m4a
Here is one that DOES work:
skdy.bryceb.dev.mediarain.com/song2.m4a
Any ideas why this works on some songs and fails on others?
Thanks MisterSquonk I'm sure that way would work.
In my particular case after beating my head against the wall for a while I realized that on some songs, I was getting to the buffered amount before the player state was getting set to prepared. So I added a check to make sure that the MediaPlayer was in the "PREPARED" state and then it worked great:
// Media prepared listener
mediaPlayer.setOnPreparedListener(
new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
setPlayerState(PlayerState.PREPARED);
}
});
// Media buffer listener
mediaPlayer.setOnBufferingUpdateListener(
new MediaPlayer.OnBufferingUpdateListener() {
public void onBufferingUpdate(MediaPlayer mp, int percent) {
// Sometimes the song will finish playing before the 100% loaded in has been
// dispatched, which result in the song playing again, so check to see if the
// song has completed first
if(getPlayerState() == PlayerState.COMPLETED)
return;
if(getPlayerState() == PlayerState.PAUSED)
return;
// If the music isn't already playing, and the buffer has been reached
if(!mediaPlayer.isPlaying() && percent > PERCENT_BUFFER) {
if(getPlayerState() == PlayerState.PREPARED)
{
mediaPlayer.start();
setPlayerState(PlayerState.PLAYING);
}
//if it isn't prepared, then we'll wait till the next buffering
//update
return;
}
}
});
OK, I hacked together a minimal Mediaplayer implementation in a 'sandbox' app/activity I always keep spare for testing.
I might be wrong but if you're streaming these songs over the net, you'll need to prefix the url with http://.
I tried the urls with Winamp and Chrome verbatim (no protocol prefix string) and they worked fine although it's likely both of those applications will use some form of intelligence to work out how to connect/stream.
If I tried that in my mediaPlayer code, I get the same exception as you but if I prefix the urls with http:// the songs play fine.
Example...
// Activity scope
Button button;
CheckBox checkBox;
String url = "";
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//button declared in my activity
button = (Button)findViewById(R.id.button);
button.setOnClickListener(this);
if (!checkBox.isChecked())
url = getString(R.string.url_song1);
else
url = getString(R.string.url_song2);
mediaPlayer = new MediaPlayer();
}
#Override
public void onClick(View arg0) {
try {
Log.i(TAG, "onClick() entered...");
mediaPlayer.setDataSource(url);
Log.i(TAG, "Preparing mediaplayer...");
mediaPlayer.prepare();
Log.i(TAG, "Starting mediaplayer...");
mediaPlayer.start();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "bad stream");
}
}
If I copy the songs to my SD card both play fine and as long as the internet url strings have an 'http://' prefix then they also work.
I am using a MediaPlayer instance in order to stream audio files from an Internet location. The audio player is in a separate activity. The user can select from a list of audio files and come to this activity to play the audio.
Now the user might go back to the previous activity (with the list) and select another audio file. In this case, I want to stop any other audio that is playing and start playing the new audio which was selected.
Is there any way I can know whether an audio file is playing without having to hold on to the MediaPlayer object?
Thanks.
Edit
I did find out how to know whether an audio is playing.
We can do it by using an object of AudioManager and calling isAudioPlaying(). This will return a true if any audio is playing.
Now the other question, how do I stop an audio currently playing? I do not have an instance of the MediaPlayer object which was created to start the audio (coz the user has already left the activity once and has come back with a new object of the activity and thus a new instance of the MediaPlayer)
You'll need to call stop() on the MediaPlayer instance. To make this work in your application, you'll either need to:
Call stop() within the audio playing activity (in onDestroy()), for example
Create a Service to play audio, and communicate with it from both activities
Using a Service will allow your code to continue running outside of the Activity life-cycle, and is the only way to persist a MediaPlayer object like you need to in this case.
Alternatively, you may be able to create a custom subclass of Application and store the MediaPlayer there, but using a Service is considered better practice.
I found way to check whether audio stream (AudioManager.STREAM_RING, AudioManager.STREAM_NOTIFICATION and etc.) is currently busy using reflection:
/**
* Unhide android api: check is stream is active now (AudioManager.STREAM_RING, AudioManager.STREAM_NOTIFICATION...),
* uses reflection
* #param audioStream
* #return
*/
public static boolean isStreamActive(int audioStream) {
Class<?> audioSystemClazz = null;
Boolean res = false;
try {
audioSystemClazz = Class.forName("android.media.AudioSystem");
if (null != audioSystemClazz) {
// isStreamActive
Method method = audioSystemClazz.getDeclaredMethod("isStreamActive", new Class<?>[] { int.class,
int.class });
if (null != method) {
res = (Boolean) method.invoke(null, audioStream, 0);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return res;
}
Here's some handy code to sleep until audio is done playing:
AudioManager manager = (AudioManager)this.getSystemService(Context.AUDIO_SERVICE);
while(manager.isMusicActive())
{
Log.d("music", "music is active");
try
{
Thread.sleep(500);
}
catch (InterruptedException e)
{
Log.e("music", "interrupted waiting for music to stop");
}
Log.d("music", "done playing music");
}
#lostintransit "Would it be better to use a service, a static variable or a singleton class? What would be the better design option?"
I think a service is what you want. The built-in media player and Pandora's app both use a service to ensure the music isn't tied to the Activity lifecycle.
If I'm understanding why you'd use a singleton or static I don't think it will accomplish what you want. The singleton/static will only be enforced within a single process in Linux. If you launch your Activity, then close it, then launch it again, they will run in different processes.
Try this...
if (mp1 != null && mp1.isPlaying()) {
mp1.stop();
}
Where mp1 is the MediaPlayer