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.
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 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);
I'm trying to start a media player that streams from a website. Right now, when "start" is pressed the entire activity just freezes for anywhere from 5 to 20 seconds while the stream connects. I'm trying to get the "player.start();" call to run in a thread to free up the activity while the stream connects but it's not working. Anyone have any ideas?
private void startplayer() {
try {
stream_player = new MediaPlayer();
stream_player.setAudioStreamType(AudioManager.STREAM_MUSIC);
stream_player = MediaPlayer.create(this, Uri.parse("http://stream-address"));
Thread thread = new Thread(new Runnable() {
public void run() {
stream_player.start();
}
});
thread.start();
SetNotification(1, "live");
liveON = true;
} catch (Exception e) {
Log.e(getClass().getName(), "Error starting to stream audio.", e);
Toast.makeText(this, "Stream seems to be offline", Toast.LENGTH_LONG).show();
}
}
Move the whole method to a separate thread. The part that takes the most time is the MediaPlayer.create() part, since this a synchronous call, which returns when the media player is ready to play.
An alternative is not to use create, but use the other format used in here which sets a listener before the prepare method, and when the listener is called, the start method is called.