I'm trying to implement the restart of MediaPlayer in Android, when errors happen (connection with server lost, network is unreachable and other).
I've seen many code examples, but all are somewhat non-standard. I think there must be the standard way to restart corresponding to the developer.android.com, but it's not clear from here, how to set the listener which would restart player on such errors.
Here are the parts of my code:
public class PlayerService extends Service implements OnErrorListener {
....
////////////////////
this.mplayer = MediaPlayer.create(c, Uri.parse(url));
mplayer.setOnErrorListener(onErrorListener);
////////////////////
MediaPlayer.OnErrorListener onErrorListener = new MediaPlayer.OnErrorListener()
{
#Override
public boolean onError(MediaPlayer mp, int what, int extra)
{
Log.e(getPackageName(), String.format("Error(%s%s)", what, extra));
playlist="ERROR";
restart();
return true;
}
};
#Override
public boolean onError(MediaPlayer player, int what, int extra) {
restart();
return true;
};
public void restart()
{
try
{
playlist="RELOADING";
for (int u=1; u<=5; u++)
{
Thread.sleep(5000);
mplayer.stop();
mplayer.release();
mplayer=null;
playSong(getApplicationContext(),currenturl);
};
}
catch (Exception e)
{
playlist="RELOADING ERROR";
}
}
//////////////
....
}
Am I setting the listener right? I'm not sure where to put onError function so I have 2 of them. When I emulate the error by setting the phone to the flight mode, the listener fires "RELOADING" and "RELOADING ERROR" title. But after the network is on, no restart of the player happens. There is no sound.
What's wrong here? The player cannot restart.
Please help to make the code workable. Also can be connection skips and IO Exception.
Overview
I ran into a similar issue and based on the documentation it indicates that all you need to do is reset your media player:
In order to reuse a MediaPlayer object that is in the Error state and recover from the error, reset() can be called to restore the object to its Idle state.
What you are currently doing is stopping and releasing (mplayer.stop() and mplayer.release()) a media player that is in the Error state. This should be causing something like an IllegalStateException to be raised. If it's not throwing an error you would still be trying to start a song in a null object. Instead of calling stop and release then setting the variable to null you should be using the mplayer.reset() function.
Another option would be to initiate a new media player but the documentation details the subtle difference between a newly instantiated MediaPlayer object and one that has had reset() called on it.
Reset after Error
Based on this information something like the following should fix your issue:
public boolean onError(MediaPlayer mp, int what, int extra)
{
Log.e(getPackageName(), String.format("Error(%s%s)", what, extra));
playlist="ERROR";
if(what == MediaPlayer.MEDIA_ERROR_SERVER_DIED)
mp.reset();
else if(what == MediaPlayer.MEDIA_ERROR_UNKNOWN)
mp.reset();
// Deal with any other errors you need to.
// I'm under the assumption you set the path to the song
// and handle onPrepare, start(), etc with this function
playSong(getApplicationContext(),currenturl);
mplayer.setOnErrorListener(this);
mplayer.setOnCompletionListener(this);
mplayer.setOnPreparedListener(this);
return true;
}
See media player constant documentation for a list of potential errors.
Setting Error Listener
As for setting the error listener, here is how I've implemented it in the past:
public class MediaPlayerActivity extends Activity implements OnCompletionListener,
OnPreparedListener, AnimationListener, OnErrorListener{
private MediaPlayer mediaPlayer;
#Override
public boolean onError(final MediaPlayer arg0, final int arg1, final int arg2) {
// Error handling logic here
return true;
}
protected void onResume(){
super.onResume();
// do some onResume logic
mediaPlayer.setOnErrorListener(this);
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setOnPreparedListener(this);
// finish on resume and start up media player
}
}
I then handle loading up the media player in another function initiated by onResume().
Related
Below is the piece of my code for handling the error of my video player. This error callback listener gets triggered for the first time only. After that, it's not capturing the error.
videoPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Toast.makeText(getApplicationContext(),
getResources().getString(R.string.msgPleaseNoConnection),
Toast.LENGTH_SHORT).show();
vVideoBufferLoader.setVisibility(View.INVISIBLE);
return false;
}
});
Note:
I tried returning true from that callback which means I handled the error. But it doesn't solve the problem too.
The goal of the MediaPlayer's OnErrorListener is to signal when an error has occurred, at which point the MediaPlayer object is in an end state.
http://developer.android.com/reference/android/media/MediaPlayer.html
If you are using the MediaPlayer constructor to 'reset' the object elsewhere in the code, you are essentially creating a new MediaPlayer object and saving it over the older one. If this is the case, then you also need to reassign the OnErrorListener.
Here's a short snippet of how I've been using OnErrorListener in my app:
private MediaPlayer.OnErrorListener vidVwErrorListener = new MediaPlayer.OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int what, int extra) { //if there was an error in trying to play the intro video
if (tryLrgClip) { // If the larger-resolution clip failed to play, try playing the backup (lower-resolution) clip.
tryLrgClip = false;
trySmClip = true;
vidVwSplashView.setVideoURI(Uri.parse("android.resource://" + getPackageName() + "/" + SPLASH_VIDEOS));
vidVwSplashView.start();
} else { // If that didn't work either, give up on playing a video, and do something else
tryLrgClip = trySmClip = false;
vidVwSplashView.setVisibility(View.GONE);
//Something else
}
return true;
}
};
I hope that helps!
Is there a way to handle the Cannot play this video in android video view programatically.
You have to implement MediaPlayer.OnErrorListener
And provide it to the following VideoView method
public void setOnErrorListener (MediaPlayer.OnErrorListener l)
It may look like this
MediaPlayer.OnErrorListener onErrorListener = new MediaPlayer.OnErrorListener()
{
#Override
public boolean onError(MediaPlayer mp, int what, int extra)
{
Log.e(getPackageName(), String.format("Error(%s%s)", what, extra));
return true;
}
};
mp the MediaPlayer the error pertains to
what the type of error that has occurred: MEDIA_ERROR_UNKNOWN MEDIA_ERROR_SERVER_DIED
extra an extra code, specific to the error. Typically implementation dependent. MEDIA_ERROR_IO MEDIA_ERROR_MALFORMED
MEDIA_ERROR_UNSUPPORTED MEDIA_ERROR_TIMED_OUT
MEDIA_ERROR_UNSUPPORTED this constant represents state you are looking for.
Returns True if the method handled the error, false if it didn't. Returning false, or not having an OnErrorListener at all, will cause
the OnCompletionListener to be called.
this following source code snippet is given:
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() {
#Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END){
activity.dismissDialog(DialogID.DIALOG_LOADING);
return true;
}
return false;
}
});
}
});
I am streaming HLS streams with Android 3.x+ devices and trying to hide a loading dialog once the buffering is completed.
The video streaming works, but the info events are never fired.
Any ideas?
I know its too late, But posting it for the users still seeking for the solution (This worked for me):
progressDialog.show();
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() {
#Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END){
progressDialog.dismiss();
return true;
} else if(what == MediaPlayer.MEDIA_INFO_BUFFERING_START){
progressDialog.show();
}
return false;
}
});
progressDialog.dismiss();
videoView.start();
}
});
You're right, the events are never fired. This is a known HLS bug that I don't think Google will fix.
This applies to the onInfo and the buffering events.
See https://code.google.com/p/android/issues/detail?id=42767 and https://code.google.com/p/googletv-issues/issues/detail?id=2
Sorry!
Not fully sure as to what the OP is asking, but here are some very untimely bits of information.
I wouldn't rely on onPrepared. I find it to be unreliable.
I have found the two most useful pieces of information for HLS streaming through the MediaPlayer are the duration of the video and the progress position of the video. You get both of these by listening to progress updates.
When the duration is greater than zero, you know the video is truly prepared and can be manipulate (scrub). When progress position changes, you know the video is done buffering and has commenced playback. This last item only works when the video is playing of course. The MediaPlayer tends to relay inaccurate information.
These pieces of information are mostly accurate and can usually be relied upon to be "fairly" timely. This timeliness varies from device to device.
onPrepared is called when the MediaPlayer is prepared to start buffering, not when the video is completely buffered. However, it is completely natural to dismiss the loading dialog from within the onPrepared method.
Also MEDIA_INFO_BUFFERING_END is used when MediaPlayer is resuming playback after filling buffers, so I do not think it should be something to use to dismiss the dialog. So this should work:
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
activity.dismissDialog(DialogID.DIALOG_LOADING);
}
});
You can able to set OnPreparedListener on videoView because its your object but if you checkout source of VideoView you will find that mMediaPlayer is its private member so any change that you do from external will not be applied to it.
As per your requirement you need buffering status so you can have thread or handler or some thing so you can update your UI to get buffer status there is one method
int percent = videoView.getBufferPercentage();
if(percent == 100){
// buffering done
}
You no need to go through setOnInfoListener
by overriding setOnPreparedListener method is enough. as in the api show
public void setOnPreparedListener (MediaPlayer.OnPreparedListener l)
Register a callback to be invoked when the media file is loaded and
ready to go.
so, you can dismiss your dialog inside setOnPreparedListener method is enough
like this
vv.setOnPreparedListener(new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(MainActivity.this, "finish11", Toast.LENGTH_LONG).show();
}
});
}
});
If you want to show loading each time it's buffering (initial time or subsequent buffer underruns) just ensure to show it again:
// at the beginning
show
boolean onInfo(int what, int extra) {
switch (what) {
case MEDIA_INFO_BUFFERING_END:
"hide";
break;
case MEDIA_INFO_BUFFERING_START
"show":
}
}
So this event sequence will do as desired:
- whenever you start (setVideoURI or start): show
- onPrepared: just plug the info listener
- onInfo BUFFERING_END hide (it's playing)
- onInfo BUFFERING_START show (it's buffering again)
- onInfo BUFFERING_END hide (it's playing)
Update:
This is assuming the info events work. Of course.
I am having some trouble using the android MediaPlayer in a non activity class, always a context error. Here is the bugged line:
MediaPlayer Shoot = MediaPlayer.create(this, R.raw.shot);
Now I know I cant use "this" in a service, but all the other stuff I tried kept giving bugs.
any suggestions?
You are too luck because in the last two days I have develop an app that uses MediaPlayer inside a background Service ;)
You can simply retrieve an instance of your MediaPlayer using the next line:
MediaPlayer mediaPlayer = new MediaPlayer();
Then you can set all listener and play song that you want.
Edit 22th april 2012
To set listeners (an example):
mediaPlayer.setOnErrorListener(new OnErrorListener() {
public boolean onError(MediaPlayer mp, int what, int extra) {
// TODO notify error to user or play next song
return true;
}
});
mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
// TODO Notify to user the completion of song or play next song
}
});
To start and play song you have to do something like this:
try{
mediaPlayer.setDataSource(mSongUrl);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
}
// Starting media player
mediaPlayer.start();
Why don't you just pass the context reference to the constructor of your non activity class.
and then from there save it and pass it to where it is needed.
Make sure your context reference no longer remain live after its use. Else it will gonna cause memory issue.
I'm currently trying to write a simple audio player that streams a URL until the user quits. Nothing fancy really, but I'm trying to use the onInfo method of MediaPlayer to wait for the metadata update flag. I have the following code for creating the media player object.
/**
* Creates a new media player and attempts to prepare it.
*/
private void createPlayer(){
Log.v(TAG, "Now in createPlayer()");
if(mPlayer==null){
Log.i(TAG, "No existing media player found, creating.");
mPlayer=new MediaPlayer();
mPlayer.setOnErrorListener(this);
mPlayer.setOnInfoListener(new OnInfoListener() {
public boolean onInfo(MediaPlayer mp, int what, int extra) {
Log.w(TAG,"---Got some info!---");
return false;
}
});
mPlayer.setOnPreparedListener(this);
mPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
} else {
Log.i(TAG, "Found an existing media player. Doing nothing.");
}
try{
mPlayer.setDataSource(mStreamUri);
mPlayer.prepareAsync();
Log.i(TAG, "Just sent the media player a prepareAsync()");
}catch(Exception e){
Log.e(TAG,"Caught exception while trying to set up media player.");
}
}
I have yet to see onError fire, but I also have yet to actually get any errors because of the simplicity of my app, but of course onPrepare works fine. I've tried implementing it with the class, as well as an inline method like the above code but nothing happens.
Change your code with this and try again -
mPlayer.prepare();
You cannot return false in onInfoListener. The android developer says if it returns false, the infoListener seems as if it was not set.