If mp.isPlaying() causes app to crush - android

I am using a MediaPlayer. Somewhere inside my code I use:
if (mp != null) {
System.out.println("This");
if (mp.isPlaying()) //1
System.out.println("That"); //2
}
And as I run it, the app for some reason stops from working. If I delete the //1 and //2 lines, is runs normally and it prints "This". But why, I just want to check if mp is playing or not

as you can see on Android MediaPlayer documentations
public boolean isPlaying ()
Added in API level 1 Checks whether the MediaPlayer is playing.
Returns true if currently playing, false otherwise Throws
IllegalStateException if the internal player engine has not been
initialized or has been released.
check if you are initialising the MediaPlayer correctly and that you haven't released it before calling isPlaying().
please mark it as correct answer if that helped you.

Related

How can I ask Android MediaPlayer not to play after onPrepared?

I am working on an Android application which does lots of concurrencies and RxJava treading in the background and it is possible to not want to play a music which is in the async preparation phase for some reason:
player.prepareAsync()
and
override fun onPrepared(mp: MediaPlayer?) {
isPreparing = false
if (playAfterPreparing) {
//start playback
mp?.start()
} else {
// don't play and keep it prepare for later!!!
}
updateNotification()
}
but the problem is after preparation you should play(!) because:
Media Player called in state 0, error (-38,0)
I even tried to start and pause one after another in preparation:
override fun onPrepared(mp: MediaPlayer?) {
Timber.i("onPrepared")
Timber.i("playAfterPreparing: $playAfterPreparing")
isPreparing = false
//start playback
mp?.start()
if (!playAfterPreparing) {
mp?.pause()
}
updateNotification()
}
but it didn't work cause it rendered to the same issue.
I also know how MediaPlayer state machine is working, but don't see anything in this situation in documents:
https://developer.android.com/reference/android/media/MediaPlayer.html
any solution for such a situation?
Thanks to #VladyslavMatviienko comments, I suspected that I am doing something wrong somewhere else and he was correct. According to Android MediaPlayer document:
It is possible to stay in Prepared state, but it is not possible to call any method namely pause() and that was my fault. In Prepared state You can only call start(), seekTo() or stop().
Hope this answer comes handy for those who might face this issue later.

Cordova media plugin - stop streaming not working - release() freezes the device

I am using cordova 6.4.0 with cordova-plugin-media for streaming radio-stations in an Android Application. Unfortunately there is a case, where the application is not responding properly anymore.
Let's say the user wants to stream a radiostation, but while the stream is loading, he wants to abort it (for example because the stream is down, or taking very long to load).
In this case I am not able to cancel the process!
media = new Media("http://direct.franceinfo.fr/live/franceinfo-midfi.mp3?ID=f9fbk29m84", mediaPlayerSuccess, mediaPlayerFail, mediaPlayerStatus);
media.play();
Now I want to cancel the process of buffering the stream, but I'm not able to. The functions:
media.pause();
media.stop();
are throwing error messages in the ADB-log and are calling the mediaPlayer-onError callback.
D/AudioPlayer( 3362): AudioPlayer Error: pausePlaying() called during invalid state: 1
...
D/AudioPlayer( 3362): AudioPlayer Error: stopPlaying() called during invalid state: 1
The media.release() command stops the loading of the stream! However just releasing the stream without stopping it, causes other, rather big problems:
Most of the times the system reacts just very slow and hangs a few seconds, if you call media.release() on a media-object. But if you do this often, the system completly freezes. Meaning it does not accetp remote-control commands anymore.
The Adb-log is still working, but does not show any errors in this case. Only the POWER-Button is still working (it locks and unlocks the screen). The only way to recover from this screwed-up state, is to reboot the device.
How am I supposed to cancel a Media-stream if it is not playing, yet? Is this a bug in the plugin?
Attached is the code-snippet, that I use to handle the media-streaming-logic. Like described above... it basically works, but it slows down or even freezes device, if you call it multiple times.
function radioControl(action, media_src){
//media_src is a webradio-streamurl.
if(action == 'play') {
// Initial Play
if(media === null){
mediaCreateObject(media_src);
}
// If we get PLAY but on antoher station
else if(media.src != media_src){
mediaReleaseRessources();
mediaCreateObject(media_src);
}
//interrupt_timer = false;
if(media === null){
mediaCreateObject(media_src);
}
media.play();
}
else if (action === 'pause') {
//If we get "pause", but it didn't even start yet
if(media._duration == -1){
mediaReleaseRessources();
}
else{
media.pause();
}
}
}
function mediaCreateObject(media_src){
media = new Media(media_src, mediaPlayerSuccess, mediaPlayerFail, mediaPlayerStatus);
}
function mediaReleaseRessources(){
media.release();
}
I found out, that this is not a cordova issue, but an 8 year-old (!) android-bug, that was never fixed. See here:
https://code.google.com/p/android/issues/detail?id=959
MediaPlayer "crash" (deadlocks the calling thread) when resetting or releasing an unused MediaPlayer
Basically the problem is: If you try to "release" a media-object that is not playing (yet), it will deadlock the calling thread, which causes the freezing that I have mentioned in the question. Unfortunately they never fixed this bug, but just marked it as "obsolete". In Android 5.1.1. the bug apparently is still there. Maybe they fixed it in later versions.
I have made a rather ugly workaround for this problem, but it is working. Basically what I did is:
We save every media-object in a javaScript-object. If the user stops it, while it plays, we can just stop and delete the object. But if it is not playing, we leave this media-object in this javaScript-object media_objects = {};
Also we save the currently active_media stream in a variable.
If cordova calls the mediaPlayerStatusChange-callback we loop through the media_objects and check if the status of one of the "pending"-objects has now changed to "running". - Cordova justs calls the media-status-change-callback without any indictation what media-object exactly just changed the state. That is unfortunate, so we have to check if one of the pending-"obsolete" objects now started playing. If so, we can stop and release it. (If the object is actually playing, stop and release works like intended - only if it's not playing, it causes the crash)
function mediaPlayerStatusChange(status){
mediaReleaseRessources();
// handle status change....
// ......
}
function mediaReleaseRessources(){
for(var key in media_objects) {
// We can only stop-and release an object, if it is playing
// If an object started playing, the "_duration"-value is != -1
if(key !== active_media && media_objects[key]._duration != -1) {
media_objects[key].stop();
media_objects[key].release();
delete media_objects[key];
}
}
}
This solution works for me, however I am still interested in a better and cleaner way to handle multiple media-streams in cordova.

What means the message "internal/external state mismatch corrected" at the MediaPlayer?

I work with a MediaPlayer and set the state of the player often programmatically like for example:
if(mp.isPlaying()) {
mp.pause();
animationPausedMusic();
}
private void animationPausedMusic() {
// Changing button image to play button
btn_play.setBackgroundResource(R.drawable.play);
... // more code
}
But sometimes the logcat gives me the message:
"internal/external state mismatch corrected"
And then the play and pause function is not working anymore.
What does this message mean? And how can I solve it?
After going through the android's native framework for media player I found that in source file mediaplayer.cpp inside function bool MediaPlayer::isPlaying() The developer is checking if the currentState of media player is in STARTED state and yet the media player is not playing any media, so it tries to change the state to PAUSED state so that the state consistency should be maintained for API users.(and here is where he is printing the message "ALOGE("internal/external state mismatch corrected");")
Now If you go through the media player state diagram below:
You would notice that this may happen when the MediaPlayer moved to 'STARTED' state after a call to start() and at this time for some obscure reason has not yet started the playback and you fire a MediaPlayer.isPlaying() method call , The Framework treat this as state inconsistency and moves to 'PAUSED' state and that's why you cannot see anything playing further.
However, if someone has some better understanding please share your thoughts!
I ran into this recently, and like some other questions say, it's this bug (marked obsolete alas)
https://code.google.com/p/android/issues/detail?id=9732
I found this error occurs when playing a MIDI file, but only sometimes. It happens when mp.isPlaying() is called quickly after mp.start()
If you can manage to not call mp.isPlaying() for a little bit, the error doesn't occur. In my case, a 10th of a second or so made the difference between getting the error or not. It's awkward, but it works.
e.g.
//setting a new track
mp.setDataSource(path);
mp.prepare();
mp.start();
//calling mp.isPlaying() here or shortly after starts the problem
//since we know it's playing, we can store that state, or call
updateUiPlaying(); //eg instead of updateUi();
//or just call some code here that takes more time first
updateScaledImages(); //something that might take time
Log.v(TAG, "mp.isPlaying = " + mp.isPlaying()); //now isPlaying() shouldn't cause that error
Also, I put a check in when I pause later.
mp.pause()
if(mp.isPlaying()){
//shouldn't be playing, must be in error
mp.stop();
mp.release();
mp = new MediaPlayer();
//any other initialization here
}
Though the problem doesn't occur if there is a wait before calling isPlaying()
Apparently there is more than one cause of this message. The following solution worked for me. It may or may not work for you. I called the method MediaPlayer.reset() immediately after instantiating the MediaPlayer object:
MediaPlayer mp = new MediaPlayer();
mp.reset();

Using Mediaplayer to replay the same file

Im hoping you can provide some guidance. I created a 'final' instance of a mediaplayer (mp1) and on a button click it plays a mp3 file. I then need to stop the file when I click a second button. This works fine until I try to play the file again - nothing happens. I think that because the mp1 instance is 'final', when I stop it, it stops for good until I relaunch the app. I dont want to pause the file, I want to stop it and then restart it afresh. Any ideas welcome. I tried putting the mp1 creation within the button. This worked until the app crashed - probably because multiple mediaplayer creations used all the device memory?
Thanks!!!
// const mediaplayer
mp1 = MediaPlayer.create(getApplicationContext(), R.raw.mysound);
...
// in button 1
if (radSound1.isChecked()) {
radSound2.setChecked(false); // ...set radiobutton 2 to false
mp1.start(); // ...play the mp3 file
}
...
// in button 2
if (mp1 != null){
mp1.reset();
//mp1.setDataSource();
// mp1.prepare();
}
Follow the state diagram here. stop() stops playing and puts you in stopped state. Before starting to play for the next time, you should call prepare() and then start() again.
So in button 1, you should call start() and in button 2, you should call stop() and prepare(). The initialization is fine, do it once and keep it outside the buttons.
Also accept answers to your questions including this so that people like me will be more motivated to reply to them in the future.

Android SDK Mediaplayer.create randomly returns null

I am having an issue where occasionally the MediaPlayer.create method will return null, even though the audio file is definitely there. In fact, if I put the call to create into a while loop, the media player will eventually be created successfully. This only seems to happen on my phone (HTC Hero running 2.1) and never in the emulator.
Is this just an issue with the phone? Or is there a different way I should be playing these audio files?
Note that I want to be loading them dynamically which is get I am making the call to getIndentifer. If there is a better way of playing these dynamically please let me know.
public void playAudio(String audioFile){
//instance variable of type MediaPlayer
_player = null;
while(_player == null){
_player = MediaPlayer.create(_context, getResources().getIdentifier(audioFile, "raw", _context.getPackageName()));
}
_player.start();
}
Thanks.
A SoundPool (Docs) might be something to consider.

Categories

Resources