I am using MediaPlayer to play a looping audio. It needs to play within a service. After several seconds it terminates. I've checked to see that my service is still running and it is. When I moved the code to an activity it worked fine and never stops. This has led me to believe that the problem has to do with threading. In the activity, MediaPlayer is running on the UI thread but in my service it runs in a thread of its own and one that is not bound to the UI thread. Perhaps this has something to do with it. I also declared my MediaPlayer object as a private static field but that doesn't help either.
Logcat only indicates that the "Media server died" with no further reason why.
If MediaPlayer needs to operate within the UI thread, how would I access that from within a service or any thread spawned by the service?
I am having this problem on Android 4.1. Here is approximately what the code looks like:
class PlaySound implements Runnable
{
private static MediaPlayer mp;
public void run()
{
this.mp = MediaPlayer.create(this.context, R.raw.sound);
this.mp.setVolume(1, 1);
this.mp.setLooping(true);
this.mp.start();
do
{
// Do something until told to stop thread.
}
while (true);
}
}
I also moved the private field to my service and even removed the do...while so that the thread could terminate immediately after playing started but this didn't help either.
EDIT:
This appears to be a problem in Android 4.1. I tested my app on 2.3 on a device from the same manufacturer and it does not experience this problem. In fact, the 2.3 device had much less resources and a slower processor, so if anything it should have been terminated if resources were an issue.
The problem here is that i create a MediaPlayer inside a thread that is managed by the Service. Use Handler(getMainLooper()) to start and stop MediaPlayer.
final Handler handler = new Handler(getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
mediaPlayer.start();
}
});
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
}
, 30 * 1000);
Related
What i have:
I have implemented three MediaPlayer.Objects in my App.
All Three are created using a thread:
protected void onResume() {
// Threads
mTT1 = new TrackThread(this, R.raw.audiofile1, 1, mHandler);
mTT2 = new TrackThread(this, R.raw.audiofile2, 2, mHandler);
mTT3 = new TrackThread(this, R.raw.audiofile3, 3, mHandler);
// start thread
mTT1.start();
mTT2.start();
mTT3.start();
super.onResume();
}
"simplified" Code in the Thread for creating:
public class TrackThread extends Thread implements OnPreparedListener {
...
...
...
public void run() {
super.run();
try {
mMp.setDataSource(afd.getFileDescriptor(),
afd.getStartOffset(), afd.getDeclaredLength());
mMp.prepare();
} catch (IllegalArgumentException | IllegalStateException
| IOException e) {
Log.e(TAG, "Unable to play audio queue do to exception: "
+ e.getMessage(), e);
}
}
As I read in several Tutorials the "prepare()" methode takes a little bit of time to finish. Therefore i implemented a "Waiting loop" which waits until all MPs are prepared and created.
When "prepare and create" are done i enable the Start button and i want to start all 3 Mediaplayers SIMULTANEOUSLY.
I again use a Thread for dooing so:
public void onClick(View v) {
// Button 1
if (mBtn.getId() == v.getId()) {
mTT1.startMusic();
mTT2.startMusic();
mTT3.startMusic();
}
Code in the thread:
public class TrackThread extends Thread implements OnPreparedListener {
...
...
...
// start
public void startMusic() {
if (mMp == null)
return;
mMp.start();
}
Please note that the code above is not the full code, but it should be enough to define my problem.
What i want, My problem:
All MPs should play their Music in Sync, unfortunately sometimes when i start the music, there is a time delay between them.
The MPs must start at the exact same time as the 3Audio-files must be played simultaneously (and exactly in sync)
What i have already tried:
+) using SoundPool: My Audio-files are to big(5Megabyte and larger) for SoundPool
+) seekTo(msec): i wanted to seek every MP to a Specific time: eg.: 0, but this did not solve the problem.
+) to reach more Programmers i also asked this question on: coderanch.com
I hope somebody can help me!
Thanks in advance
The bottleneck here will certainly be preparing the mediaplayers to play. The Android framework provides an asynchronous method to perform this loading, and so with a bit of synchronization code you should be able to get these audio sources to play at roughly the same time. To keep from sound artifacting, you'll want less than 10ms of latency.
Initialize an atomic counter, C, to the number of things to load.
Use the prepareAsync() functions within MediaPlayer to prepare all three. Immediately after calling prepareAsync, supply a listener using setOnPreparedListener(listener).
Inside this listener, decrement C and check the value. If the value is greater than 0, wait on an object using the java object .wait() function. If the value is equal to 0, call notifyAll() on the object to wake up all of the other mediaplayer prepared-listener callback threads.
public void startMediaPlayers(List<MediaPlayer> mediaPlayers) {
private AtomicInteger counter = new AtomicInteger(mediaPlayers.size());
Object barrier = new Object();
/* start off all media players */
for (MediaPlayer player : mediaPlayers) {
player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(final MediaPlayer mediaPlayer) {
int value = counter.decrementAndGet();
if (value == 0) {
// all media players are done loading.
// wake up all the ones that are asleep
barrier.notifyAll();
} else {
while (value > 0) {
try {
// wait for everyone else to load
barrier.wait();
} catch (InterruptedException e) {
// ignore
}
}
}
mediaPlayer.start();
callback.success(true);
}
player.prepareAsync();
}
}
As nobody could help me I found a solution on my own. MediaPlayer did not fulfill my requirements but Android JETPlayer in combination with JETCreator did.
CAUTION: Installing Python for using JETCreator is very tricky, therfore
follow this tutorial. And be careful with the versions of python and wxpython, not all versions support the JETCreator.
I used:
Python Version 2.5.4 (python-2.5.4.msi)
wxPython 2.8 (wxPython2.8-win32-unicode-2.8.7.1-py25.exe)
For those who do not know how to implement the Jetplayer watch this video
(at min.5 he starts with programming the Jetplayer).
Unfortunately I do not speak French so I just followed the code which worked for me.
Using Android JETCreator you can create your own JET Files and use them as your resource.
Useful links:
Demo data
Manual
Code/class
I am able to play an mp3 file using android's MediaPlayer object. But I would like to play between a range of milliseconds for example between 30000 ms to 40000 ms ( 10 seconds only ). How can I achieve this?
Currently the following code is what I have,
private MediaPlayer mPlayer;
public void play() {
try {
mPlayer = MediaPlayer.create(getApplicationContext(), R.raw.mp3_file);
if (mPlayer != null) {
int currentPosition = mPlayer.getCurrentPosition();
if (currentPosition + 30000 <= mPlayer.getDuration()) {
mPlayer.seekTo(currentPosition + 30000);
} else {
mPlayer.seekTo(mPlayer.getDuration());
}
mPlayer.start();
}
}
catch(Exception e) {
}
}
Any help is greatly appreciated. Thank you!
You can use the method:
public int getCurrentPosition ()
to obtain the current time in milSeconds maybe inside a Handler that runs every 1000 milSeconds and tests to see:
if(mPlayer.getCurrentPosition() >= (mPlayer.getDuration + 40000));
Dont forget to release the media file when you're done using it:
public void release();
mPlayer.release();
Releases resources associated with this MediaPlayer object. It is
considered good practice to call this method when you're done using
the MediaPlayer. In particular, whenever an Activity of an application
is paused (its onPause() method is called), or stopped (its onStop()
method is called), this method should be invoked to release the
MediaPlayer object, unless the application has a special need to keep
the object around. In addition to unnecessary resources (such as
memory and instances of codecs) being held, failure to call this
method immediately if a MediaPlayer object is no longer needed may
also lead to continuous battery consumption for mobile devices, and
playback failure for other applications if no multiple instances of
the same codec are supported on a device. Even if multiple instances
of the same codec are supported, some performance degradation may be
expected when unnecessary multiple instances are used at the same
time.
The best approach is to use a Handler to time the stopping of the playback. Start the player and then use the Handler's postDelayed to schedule the execution of a Runnable that will stop the player. You should also start the player only after the initial seek completes. Something like this:
public class PlayWord extends Activity implements MediaPlayer.OnSeekCompleteListener {
Handler mHandler;
MediaPlayer mPlayer;
int mStartTime = 6889;
int mEndTime = 7254;
final Runnable mStopAction = new Runnable() {
#Override
public void run() {
mPlayer.stop();
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final TextView tv = new TextView(this);
tv.setText("Playing...");
setContentView(tv);
mHandler = new Handler();
mPlayer = MediaPlayer.create(this, R.raw.nicholas);
mPlayer.setOnSeekCompleteListener(this);
mPlayer.seekTo(mStartTime);
}
#Override
public void onDestroy() {
mPlayer.release();
}
#Override
public void onSeekComplete (MediaPlayer mp) {
mPlayer.start();
mHandler.postDelayed(mStopAction, mEndTime - mStartTime);
}
}
Note also that the MediaPlayer.create method you are using returns a MediaPlayer that has already been prepared and prepare should not be called again like you are doing in your code.on the screen. I also added a call to release() when the activity exits.
Also, if you want to update the UI when the seek completes, be aware that this method is usually called from a non-UI thread. You will have to use the handler to post any UI-related actions.
I'm copied this from: Android: How to stop media (mp3) in playing when specific milliseconds come?
I start the mediaplayer to play a piece of music in a thread and I'm wondering why mediaplayer can continue working even the thread is already dead. Here is the example:
public class MusicThread extends Thread {
MediaPlayer mp;
public MusicThread(Context context) {
mp = MediaPlayer.create(context, R.raw.music);
}
#Override
public void run() {
mp.start();
Log.d("MusicThread", "mp started");
}
}
Then inside the activity:
MusicThread musicThread = new MusicThread(this);
musicThread.start();
Here is my confusion:
After musicThread.start(), the music begins. Also, the thread completes becasue we can see the log generated by Log.d(...) in LogCat and we can see the false returned by musicThread.isAlive().
I have the reference to musicThread such that it won't be GC immediately when it finishes.
But what about the mediaplayer? It continues working but the thread which it resides has died already. It seems that it's in a weird state in this case. Is it still working in musicThread? If yes, why and how? If not, where it is?
mp.start() is not a blocking call. So your thread won't wait until playing is finished. mp.start() call returns immediately. You don't even need a separate thread to call it.
PS : If you want to get a callback when the playing is finished just use setOnCompletionListener. Once the playing is completed, public void onCompletion(MediaPlayer mp) will be called
I'm trying to write a function to play a short sound (in /res/raw) in my program, called at effectively random times throughout the program. So far I have this function:
public void playSound() {
MediaPlayer mp = new MediaPlayer();
mp = MediaPlayer.create(this, R.raw.ShortBeep);
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
mp.setLooping(false);
mp.start();
}
It works fine for awhile, but after exactly 30 plays of the sound, it stops making sound.
According to the Docs
... failure to call release() may cause subsequent instances of MediaPlayer objects to fallback to software implementations or fail altogether.
When you are done with it call mp.release() so that it can release the resources. I don't know what the limit is and I'm sure it depends on many factors. Either way you should be calling this function on your MediaPlayer object, especially if it will be used more than once.
I've just solved the exact same problem, but I'm using Xamarin. I ended up changing from holding on to a MediaPlayer instance for the lifetime of the activity to creating an instance each time I want to play a sound. I also implemented the IOnPreparedListener and IOnCompletionListener.
Hopefully you can get the idea despite it being C# code
public class ScanBarcodeView :
MvxActivity,
MediaPlayer.IOnPreparedListener,
MediaPlayer.IOnCompletionListener
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.ScanBarcodeView);
((ScanBarcodeViewModel) ViewModel).BarcodeScanFailed += (sender, args) => PlaySound(Resource.Raw.fail);
((ScanBarcodeViewModel) ViewModel).DuplicateScan += (sender, args) => PlaySound(Resource.Raw.tryagain);
}
private void PlaySound(int resource)
{
var mp = new MediaPlayer();
mp.SetDataSource(ApplicationContext, Android.Net.Uri.Parse($"android.resource://com.company.appname/{resource}"));
mp.SetOnPreparedListener(this);
mp.SetOnCompletionListener(this);
mp.PrepareAsync();
}
public void OnPrepared(MediaPlayer mp)
{
mp.Start();
}
public void OnCompletion(MediaPlayer mp)
{
mp.Release();
}
}
So, each time I want a sound to be played I create a MediaPlayer instance, so the data source, tell it that my Activity is the listener to Prepared and Completion events and prepare it. Since I'm using PrepareAsync I don't block the UI thread. When the media player is prepared the Start method on the MediaPlayer is called, and when the sound has finished playing the MediaPlayer object is released.
Before I made these changes I would get to 30 sounds played and it would all stop working. Now I've gone way past 30, also multiple sounds can be played simultaneously.
Hope that helps.
I've a multi-threaded app where main thread initiate two threads:
MakeRequest Thread
QueueListener Thread
MakeRequest thread after each second query a printer on LAN to request some data and perform some calculations on it and feed it to a Queue on which the second thread is listening. As soon as the data is available in the Queue, QueueListener thread dequeue a record from the Queue and initiate another thread i.e. MediaPlayer thread this thread is responsible for playing 7 to 8 files depending on the string received. For which I am using the following code.
MediaPlayer mp = MediaPlayer.create(context, Uri.fromFile(new File(q[voiceIndex])));
mp.setOnCompletionListener(mCompletionListener);
mp.setOnErrorListener(mErrorListener);
mp.start();
this code is in PlayMedia() method, and in OnCompletionListener, I've the following code:
OnCompletionListener mCompletionListener = new OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
//will be called when media player finished playing a file.
mp.release();
StartPlayingNextFile();
}
};
private void StartPlayingNextFile() {
voiceIndex++;
if (voiceIndex < q.length){
PlayMedia();
}else{
finishedPlaying = true;
}
}
When QueueListener Thread initiate MediaPlayer Thread I've used Join() on MediaPlayer thread in order not to dequeue another string from the queue and wait till the MediaPlayer finishes its business, otherwise I'll hear over lapping sounds.
Now, most of the time everything seems to be working fine but MediaPlayer sometime skips playing some files and thus MediaPlayer thread never terminates because OnCompletionListener never called and OnErrorListener never get called either, because of which Join() never releases, so I've to explicitly do it after a reasonable time has passed:
#Override
public void run() {
//record the start time
timeStart = new Date().getTime();
PlayMedia();
while (!finishedPlaying){
try {
//if a reasonable time has passed break the loop
long currentTime = (new Date().getTime() - timeStart);
long elapsedSeconds = TimeUnit.MILLISECONDS.toSeconds(currentTime);
if (elapsedSeconds > 15){
break;
}
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Log from LogCat can bee seen here where 5 files have been skipped between line 2 and 27 i.e.
Line 2:
01-21 01:20:18.474: V/MediaPlayerService(3240): Create new client(1854) from pid 15063, url=/mnt/sdcard/voicedata/200.wav, connId=1854
Line 27:
01-21 01:20:30.504: V/MediaPlayerService(3240): Create new client(1855) from pid 15063, url=/mnt/sdcard/voicedata/constants/bell.wav, connId=1855
files that have been skipped are:
/mnt/sdcard/voicedata/a.wav
/mnt/sdcard/voicedata/b.wav
/mnt/sdcard/voicedata/c.wav
/mnt/sdcard/voicedata/d.wav
/mnt/sdcard/voicedata/e.wav
and bell.wav is the very first file, that plays before all these files.
After rigorous testing I've found out that reducing the number of files actually improves the changes that MediaPlayer would not skip any file.