The only difference I notice is that, if I call prepare() before play(), I will see the the process indicator and it pre-loads data in the PlayerView, besides that I can't tell the difference if I just call play() without prepare().
Also the documentation says nothing: https://exoplayer.dev/doc/reference/com/google/android/exoplayer2/SimpleExoPlayer.html
public void prepare()
Description copied from interface: Player
Prepares the player. //<- ???
Specified by:
prepare in interface Player
Related
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();
While searching about internal details of video player, I came across a pdf where MediaPlayer class internally uses android_media_Mediaplayer for every message(i.e, setDataSource(), prepare(), start() etc.) and android_media_MediaPlayer calls libmedia::MediaPlayer() with same message. My question is why can't MediaPlayer class directly call libmedia::MediaPlayer instead calling through android_media_MediaPlayer?
Thank you!
The link of image is given below...
http://img600.imageshack.us/img600/2005/capturejij.png
The diagram you linked to wasn't crystal clear, but I assume that the blue MediaPlayer box refers to the MediaPlayer Java class.
The libmedia MediaPlayer is a native class. Calls between Java and C/C++ need to go through the Java Native Interface (JNI), so android_media_MediaPlayer contains the necessary JNI code to communicate with the MediaPlayer Java class, thereby acting as a sort of proxy between the Java class and the native libmedia class.
For example, in MediaPlayer.java you'll find this delcaration:
public native void prepareAsync() throws IllegalStateException;
Which is listed in android_media_MediaPlayer as a JNINativeMethod:
{"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync},
This says that the method which Java knows as "prepareAsync" has the signature "()V" (no arguments, returns void) and corresponds to the native function android_media_MediaPlayer_prepareAsync. When android_media_MediaPlayer_prepareAsync is called, it in turn calls the native MediaPlayer's prepareAsync method.
We're using MediaRecorder to record video to a file on the external storage using setOutputFile() before doing the actual recording.
Everything works fine, but the main issue is that as soon as the recording is done, we want to start playing the recorded video back in a VideoView.
How to know when the file is ready to be read and played back?
The FileObserver class suits your needs perfectly. Here is the documentation. It's easy to use. When a observed file is closed after writing, the onEvent callback is called with CLOSE_WRITE as the parameter.
MyFileObserver fb = new MyFileObserver(mediaFile_path, FileObserver.CLOSE_WRITE);
fb.startWatching();
class MyFileObserver extends FileObserver {
public MyFileObserver (String path, int mask) {
super(path, mask);
}
public void onEvent(int event, String path) {
// start playing
}
}
Don't forget to call stopWatching().
We solved similar problem with the following algo:
while (file not complete)
sleep for 1 sec
read the fourth byte of the file
if it is not 0 (contains 'f' of the 'ftyp' header) then
file is complete, break
The key point is that MediaRecorder writes the ftyp box at the very last moment. If it is in place, then the file is complete.
In my tests irrespective of the size of the recording mediaRecorder.stop() is a blocking method that only returns after the file has been completely written and closed by the media recorder.
So JPMs answer is actually correct.
You can verify this by calling File.length() immediately after stop(). You will find that the output file length is the final length of the file at this point. In other words media recorder does not write anything further to the file after stop() has returned.
I haven't tried this myself but this might work:
public void release () Since: API Level 1
Releases resources associated with this MediaRecorder object. It is
good practice to call this method when you're done using the
MediaRecorder.
If it does what it says, then I guess if you call this and after this method returns you know the file is ready.
Apparently there is no way to detect when the recording has stopped in Media player, but there is a stop() that you can override if you create a custom class that implements MediaRecorder. here I would do something like this:
public class MyRecorder implements MediaRecorder {
public boolean stopped;
.... implement all the methods that MediaRecorder has making
sure to call super for each method.
#Override
public void myStop() {
this.stopped = true;
super.stop();
}
}
Then you can access the boolean to see if it has stopped recording.
A dirty way would be to check the lastModified() value of the File and open the VideoView if the File wasn't modified for 2 seconds.
I have an application with a VideoView that will keep looping the same video over and over until a user does something to the device(touch screen, etc) Currently I am using the OnCompletionListener() to restart the video once it ends. This works properly on every device I have tested, except for the Samsung Replenish.
Here is my code:
mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
mVideoView.setVideoPath(file.getAbsolutePath());
mVideoView.start();
}
});
What happens on the Replenish is the video plays all the way through once but then is never started again and the screen goes all black (But backlight still turned on). On every other device I've tested on with this exact same code it has worked to repeat the video. Does anyone know of anything specific about the Replenish that could cause this error? I thought maybe delaying the calls to setVideoPath(), and start() by 200-300ms might help it, but that had no affect. I am really at a loss here.
I am seeing these messages in my Log:
ERROR/QCvdec(87): Omx Flush issued when vdec is not initialized yet.
ERROR/QCvdec(87): OMXCORE-SM:Recieved command DISABLE (2)
ERROR/QCvdec(87): Omx Flush issued when vdec is not initialized yet.
ERROR/QCvdec(87): OMXCORE-SM:Recieved command ENABLE (3)
But these logs are happening both when the video starts (the first time it plays) and when it fails to start again. so I am not sure if they are related to my problem
Edit:
I just tried setting mVideoView to null, and then getting a new reference to it with findViewById() right before the setVideoPath(). I know this would complicate the way the OnCompletionListener is set up. But regardless of that it didn't work anyway, still the same dark screen.
Edit 2:
I've started to notice that sometimes the video doesn't even start the first time. I am using these same two lines to start it up the first time:
mVideoView.setVideoPath(file.getAbsolutePath());
mVideoView.start();
It seems to start more consistantly, but not quite 100% when its the first time it is being played.
Edit 3: This is how I have it set up now. I am manually setting the OnPreparedListener to start the video for me. So I added this to my onCreate()
mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer arg0) {
mVideoView.start();
}
});
Then when I am ready to restart the video I just call only the setVideoPath() method, Like this:
mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
mVideoView.setVideoPath(file.getAbsolutePath());
}
});
This seems to be doing the trick. I am letting it run for a while to find out for sure though.
Edit 4:
#MByD In the code for VideoView setVideoPath() is a wrapper for setVideoUri(). setVideoURI() is setting mStartWhenPrepared = false; The default OnPreparedListener checks this switch to decide whether to start playback or not. That is why it doesn't start with the default listener. I haven't looked into it more than that, but there may be a setter method that lets me change the mStartWhenPrepared value to true, which would cause the video to be started from the default listener.
Have you tryed to prepare() your video before you call .start()?
from the docs: http://developer.android.com/reference/android/media/MediaPlayer.html#prepare%28%29
with a OnPreparedListener you can start your video when its ready.
I'm working on writing a small app that will stream mp3 files. I'm using the NPR code, but having a strange problem with mediaPlayer.prepareAsync().
I'm using a trimmed down version of the PlaybackService from the NPR app, which is getting started correctly. I am getting a reference to the service inside an OnClick handler inside an Activity, and calling listen() with the URL to the MP3 stream. The following (simplified) code is from my PlaybackService:
public void listen(String url) throws IllegalArgumentException, IllegalStateException, IOException {
if (mediaPlayer == null) {
mediaPlayer = new MediaPlayer();
}
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setOnErrorListener(this);
mediaPlayer.setOnInfoListener(this);
mediaPlayer.setOnPreparedListener(this);
synchronized (this) {
mediaPlayer.setDataSource(url);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.prepareAsync();
}
}
// ... lots of other code
#Override
public void onPrepared(MediaPlayer mp) {
Log.d(LOG_TAG, "Prepared");
play();
}
I have the other callbacks defined as well. I can see from LogCat that the MediaPlayer is loading the stream and buffering, as I see the following messages:
AwesomePlayer I calling prefetcher->prepare()
AwesomePlayer I prefetcher is done preparing
But my onPrepared method never gets called. If I add a timer and try to call play() on the MediaPlayer at some point after I see the above log messages, the media players plays, so it is indeed entering into the Prepared state.
If I replace the call to prepareAsync() with prepare(), the player just works. This is all on 2.2, which I have been reading seems to have some issues, but the problem I'm having doesn't seem related, as the stream works fine when prepare() is used.
I did notice that the Content-Length on the the stream is quite large (450MB), but since I can call play on the Media Player without getting an exception, it appears to be handling this OK.
The only other change is that in the NPR app, the service is being bound to and the playback started from inside a View object (while in my app, this happens inside an Activity).
Any thoughts on what I could be doing wrong?
Make sure you have created the mediaplayer in a thread that has a looper, which is required for the callbacks to work properly.