I have been stuck with this problem for quite a while now. In the mediaPlayer i've created everything runs smooth in the initial playing stage but when the user selects the next or previous option, the application gives me a (0,-107) error along with the attempt to call getDuration without a valid mediaplayer
The code:
public void playPodCast(int index) {
try {
urlToSend = toGet.get(index).get("URL").toString();
mediaPlayer.reset();
mediaPlayer.setDataSource(urlToSend);
mediaPlayer.prepareAsync();
onPlayerLoad();
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.setOnErrorListener(this);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
here the onPlayerLoad() is just a method to hide all the views while the mediaPlayer is loading.
#Override
public void onPrepared(MediaPlayer mp) {
// TODO Auto-generated method stub
onPlayerLoaded();
updateProgressBar();
mediaPlayer.start();
}
the onPlayerLoaded() loads back on the view.
public void updateProgressBar() {
seekHandler.postDelayed(updateSeekTime, 100);
}
private Runnable updateSeekTime = new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
long totalDuration = mediaPlayer.getDuration();
long currentDuration = mediaPlayer.getCurrentPosition();
// Displaying the total time duration
seekRight.setText("" + utils.milliSecondsToTimer(totalDuration));
// Displaying completed playing time
seekLeft.setText("" + utils.milliSecondsToTimer(currentDuration));
// Updating the SeekBar progress
int progress = (int) (utils.getProgressPercentage(currentDuration,
totalDuration));
seekBar.setProgress(progress);
seekHandler.postDelayed(this, 100);
}
};
#Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// TODO Auto-generated method stub
}
// #Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
seekHandler.removeCallbacks(updateSeekTime);
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
seekHandler.removeCallbacks(updateSeekTime);
int totalDuration = mediaPlayer.getDuration();
int currentPosition = utils.progressToTimer(seekBar.getProgress(),
totalDuration);
// forward or backward to certain seconds
mediaPlayer.seekTo(currentPosition);
// update timer progress again
updateProgressBar();
}
Here, private Handler seekHandler = new Handler();
Like I said before, the initial call to playPodCast(index) does not give me any errors, it works very smoothly. But when the user clicks on the next and previous buttons, the error is generated. The next and previous methods do not give out any errors if implemented without the updateProgressBar(), everything runs unbuggyly. So why am I getting this error ? I have no clue why ... please do help.
Here's the implementation of the next button:
btnNext.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
if (getPosition == totalList - 1) {
getPosition = 0;
playPodCast(getPosition);
} else {
getPosition = getPosition + 1;
playPodCast(getPosition);
}
}
});
where getPosition is the position of the media file being referenced.
And I also did try
#Override
public void onPrepared(MediaPlayer mp) {
// TODO Auto-generated method stub
mediaPlayer.start();
onPlayerLoaded();
updateProgressBar();
}
Update 1:
I found that the error occurs when using the prepareAsync(), the program works fine on prepare() but is super laggy.
The problem is getDuration is getting called by your updateSeekTask while the new media is preparing. This will error. What's the duration of an unprepared video?
The solution is to cancel any callbacks of the updateSeekTask when the user clicks next, and reinstate them once the media is ready.
public void onClick(View arg0) {
if (getPosition == totalList - 1) {
getPosition = 0;
seekHandler.removeCallbacks(updateSeekTime); // Cancel me!
playPodCast(getPosition);
} else {
getPosition = getPosition + 1;
seekHandler.removeCallbacks(updateSeekTime); // Cancel me!
playPodCast(getPosition);
}
}
Then in onPrepared restart the task as you do currently:
public void onPrepared(MediaPlayer mp) {
mediaPlayer.start();
onPlayerLoaded();
updateProgressBar();
}
You should still use prepareAsync. If you just use prepare everything will wait until the media is prepared. This is the lag you experience. It will fix your problem (since even the task will wait) but it's not recommended.
Instead of calling the following:
int totalDuration = mediaPlayer.getDuration();
inside the Runnable updateSeekTime() make the totalDuration a global variable and refer initialize the value of the totalDuration inside the onPrepared(MediaPlayer mp) like this:
#Override
public void onPrepared(MediaPlayer mp) {
// TODO Auto-generated method stub
onPlayerLoaded();
totalDuration = mp.getDuration() // mp is the MediaPlayer instantiated by this method
updateProgressBar();
mediaPlayer.start();
}
and Presto you are done ... Hope this helps someone.
Related
I use media player to play sounds on my application. It works just fine.
I am playing sound on a separated thread. Even thought, part of the sound plays before activity appear.
I tried to play sound onCreate method. it didn't work. onStart and
onResume. it has some problems. it plays every time activity resumed.
sometimes while I am not even touching the device!
What is the best way to play sound after activity appears?
public boolean played = false;
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
if (!played)
Settings.playSound(dvd.titleImageName.replace("png", "mp3"), this);
played = true;
}
public static MediaPlayer mp = null;
public static void playSound(String fileName, Context c)
{
//MediaPlayer mp = MediaPlayer.create(c, resId);
if (mp!=null)
{
mp.stop(); //error
mp.reset();
mp.release();
}
mp = new MediaPlayer();
AssetFileDescriptor descriptor;
try {
descriptor = c.getResources().getAssets().openFd("sounds/" + fileName);
mp.setDataSource(descriptor.getFileDescriptor(), descriptor.getStartOffset(), descriptor.getLength());
descriptor.close();
mp.prepare();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (mp == null) return;
mp.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
}
});
mp.setOnPreparedListener(new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
// TODO Auto-generated method stub
}
});
mp.setOnVideoSizeChangedListener(new OnVideoSizeChangedListener() {
#Override
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
// TODO Auto-generated method stub
}
});
new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
mp.start();
}
}).start();
}
Your played variable only works while activity is alive. If it gets killed, played doesn't retain. For example, rotate your device an you'll hear sound playing again.
save state (played) in onSavedInstanceState bundle and restore it in onCreate
Play in in OnCreate or onResume
When my mediaplayer starts playing, I want to get the time elapsed from the beginning of the player till the end of the media being played. For example 00:01, 00:02, 00:03, 00:04... How can i achieve this? Below is my mediaplayer class.
public class EntityPageActivity extends Activity implements Runnable, OnClickListener, OnSeekBarChangeListener{
private SeekBar seekBar;
private Button startMedia;
private Button pauseMedia;
private MediaPlayer mp;
Uri url;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sample);
AudioControl();
}
public void AudioControl(){
seekBar = (SeekBar) findViewById(R.id.seekBar);
startMedia = (Button) findViewById(R.id.play);
pauseMedia = (Button) findViewById(R.id.pause);
startMedia.setOnClickListener(this);
pauseMedia.setOnClickListener(this);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onStopTrackingTouch(SeekBar arg0) {
// TODO Auto-generated method stub
//System.out.println("maxvolume"+Integer.toString(maxVolume));
}
#Override
public void onStartTrackingTouch(SeekBar arg0) {
// TODO Auto-generated method stub
}
#Override
public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {
// TODO Auto-generated method stub
mp.seekTo(arg1);
seekBar.setProgress(arg1);
}
});
}
public void run() {
int currentPosition= 0;
int total = mp.getDuration();
while (mp!=null && currentPosition<total) {
try {
Thread.sleep(1000);
currentPosition= mp.getCurrentPosition();
} catch (InterruptedException e) {
return;
} catch (Exception e) {
return;
}
seekBar.setProgress(currentPosition);
}
}
public void onClick(View v) {
if (v.equals(startMedia)) {
if (mp != null && mp.isPlaying()) return;
if(seekBar.getProgress() > 0) {
mp.start();
return;
}
mp = MediaPlayer.create(EntityPageActivity.this,Uri.parse("http://cdn-preview-e.deezer.com/stream/ed403858b3532e9c07c99d7015269848-2.mp3"));
mp.start();
seekBar.setProgress(0);
seekBar.setMax(mp.getDuration());
new Thread(this).start();
}
if (v.equals(pauseMedia) && mp!=null) {
mp.pause();
}
}
#Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// TODO Auto-generated method stub
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
}
Here are these two methods one is to get Media Player Current Position and second is to get total duration
// returns current position
mediaPlayer.getCurrentPosition();
//returns total duration
mediaPlayer.getDuration();
hope this will help
Implement a runnable when you start your mediaplayer:
mp.start();
Handler handler = new Handler();
Runnable runnable = new Runnable() {
public void run() {
while (mp.isPlaying()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.post(new Runnable() {
public void run() {
seekbar.setMax(mp.getDuration() / 1000);
currTime.setText(getDurationString(mp
.getCurrentPosition() / 1000));
seekbar.setProgress(mp
.getCurrentPosition() / 1000);
}
});
}
}
};
new Thread(runnable).start();
Everything works perfectly, only problem is pausing. I wanted to repeat some long media for 5 times and also I wanted to pause the media in-between whenever I need.
But when I press pause/play button the playsound(); method is called again and again so the music plays more than 5 time.
This is my code
ImageButton bv.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
if(mp.isPlaying()){
mp.pause();
bv.setImageResource(R.drawable.play);
} else {
bv.setImageResource(R.drawable.pause);
playsound();
}
}
});
public void playsound() {
mp.setOnCompletionListener(new OnCompletionListener() {
int count = 1;
#Override
public void onCompletion(MediaPlayer mp) {
// TODO Auto-generated method stub
if(count < 5) {
count++;
mp.start();
mp.seekTo(0);
}
}
}); mp.start();
}
Any idea how to fix this?
I'm playing video via MediPlayer in my android application and have SeekBar displayed. Now I want this seeks bar to automatically update as the video progresses so it should automatically move from left to right. At the moment, (code below) the bar updates and this is done via running thread, that every second updates the progress of seekBar. The problem is it is not smooth and as seekBar is updated via its seekProgress() the video stops for split second and all is very jumpy. Now I would like it to have updated more often then every second as well as keep functionality that I already implemented to allow user to tap on the bar and change progress of the video.
I'm after something like Android MediaPLayer application have, seekBar is on transparent background and all is smooth and I have no idea how it is done.
No, currently as you see from the code below thread updates every second as it sleeps inside f run method. I've also tried to use handlers to update UI thread, effect was the same. I also extended SeekBar to its own class, had thread there and this was no good either, exactly same effect.
If anyone can explain to me how to solve this problem and how its done with other player appls that would be great.
public class FightPlayerActivity extends Activity implements Runnable, OnSeekBarChangeListener, SurfaceHolder.Callback, OnPreparedListener {
private MediaPlayer mp=null;
private SeekBar seekBar;
private Thread progressBarUpdater;
private String filePath;
private Handler handler=new Handler();
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Toast.makeText(this,"Create ", 2000).show();
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
public void onStop()
{
super.onStop();
mp.stop();
mp.reset();
mp.release();
}
public void run()
{
while(true)
{
try {
progressBarUpdater.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
seekBar.setProgress(mp.getCurrentPosition());
// handler does have same effect, so video stops for split second
//handler.postDelayed(this, 1000);
}
}
public void onStart()
{
super.onStart();
setContentView(R.layout.fight_player);
filePath=getIntent().getStringExtra("filename");
filePath=Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)+"/FightAll_BJJ_Scoring/"+filePath;
Toast.makeText(this,filePath, 2000).show();
// seek bar
seekBar=(SeekBar) findViewById(R.id.seek_bar);
seekBar.setOnSeekBarChangeListener(this);
try {
SurfaceView sv=(SurfaceView) findViewById(id.video_preview);
SurfaceHolder sh=sv.getHolder();
sh.addCallback(this);
sh.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void stop(View view)
{
mp.seekTo(0);
mp.pause();
}
public void pause(View view)
{
mp.pause();
}
public void play(View view)
{
mp.start();
}
public void surfaceCreated(SurfaceHolder holder) {
try {
mp=new MediaPlayer();
mp.setDataSource(filePath);
mp.setDisplay(holder);
mp.setOnPreparedListener(this);
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
mp.prepare();
//handler.removeCallbacks(this);
//handler.postDelayed(this, 1000);
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
public void onPrepared(MediaPlayer mediaplayer) {
mp.start();
seekBar.setMax(mp.getDuration());
progressBarUpdater=new Thread(this);
progressBarUpdater.start();
//handler.postDelayed(this, 1000);
}
public void onProgressChanged(SeekBar sb,int progress,boolean fromUser)
{
//Toast.makeText(this, progress, 2000).show();
mp.seekTo(progress);
}
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
onProgressChanged(seekBar,seekBar.getProgress(),true);
}
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
}
Your major problem is in your onProgressChanged() method.
You are seeking to the specified position every time the seekBar progress changes, even when it is done programmatically. Which means that every time you call seekBar.setProgress(mp.getCurrentPosition()), onProgressChanged() will be fired.
So we change it to the following:
public void onProgressChanged(SeekBar sb, int progress, boolean fromUser) {
if (fromUser) {
mp.seekTo(progress);
}
}
That way it will only be fired when the user moves the seekBar.
Moreover, according to this answer, it would be better to replace your while(true) loop with:
public void run() {
seekBar.setProgress(mp.getCurrentPosition());
if (mp.getCurrentPosition() < mp.getDuration()) {
seekBar.postDelayed(this, MILLISECONDS);
}
}
I have some Android code to stream an audio file from the internet and play the stream after 10 seconds.
I am using a SeekBar to view the buffering status and playing status. I want to play the audio starting from the middle of the buffered stream. For that, I move the SeekBar point to the middle, but I cannot play the audio from the middle; it will go back and start from the beginning. How can I get the seeked position and how can I play the audio from that particular position?
Here is my SeekBar code. How can I make this code use the OnSeekBarChangeListener properly?
seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {
// TODO Auto-generated method stub
if (arg2 && mediaPlayer.isPlaying()) {
//myProgress = oprogress;
arg1=mediaPlayer.getCurrentPosition();
mediaPlayer.seekTo(arg1);
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
});
arg1=mediaPlayer.getCurrentPosition();
mediaPlayer.seekTo(arg1);
You are forcing the player to seek to the current position, and not to the positon retuned by the Seekbar
Remove the line: arg1=mediaPlayer.getCurrentPosition(); and it should work. Of course after MediaPlayer.prepare() set SeekBar.setMax(MediaPlayer.getDuration()), so seeking will be accurate.
I think you have to make a thread... I have some code given below which you can try to implement.
public void run() {
try
{
while(song1.getDuration()!=song1.getCurrentPosition())
{
skbar.setProgress(song1.getCurrentPosition());
//bStop.setText(song1.getCurrentPosition());
}
if (song1.getDuration()==song1.getCurrentPosition())
Log.v("log","Sanket");
t.suspend();
}
catch (Exception e)
{
Log.e("log",e.toString());
}
}