I need to catch touch events to the MediaController progress slider, so that updates to the MediaPlayer don't occur until the finger lifts off the slider.
[ Perhaps my situation is unique: I am playing streaming video that comes in multiple "stacks" for each "program". The next stack is not loaded until the previous one is finished. The slider needs to represent the duration of all stacks, and the progress of the "thumb" needs to represent total duration. This is easily done by overriding the getBufferPercentage(), getCurrentPosition(), and getDuration() methods of MediaPlayerControl ]
More problematic is "scrubbing" (moving the thumb) back and forth along the timeline. If it causes the data source to be set multiple times along with a seekTo for every movement, things very quickly bog down and crash. It would be better if the MediaPlayer did no action until the user has finished the scrub.
As others have written, Yes it would be best to write my own implementation of the MediaController. But why redo all that work? I tried extending MediaController, but that got complicated quickly. I just wanted to catch the touch events to the slider!
One is able to get a handle to the elements of MediaController:
final int topContainerId1 = getResources().getIdentifier("mediacontroller_progress", "id", "android");
final SeekBar seekbar = (SeekBar) mController.findViewById(topContainerId1);
Then set a listener on the seekbar, which will require you to implement its three public methods:
seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// Log.i("TAG", "#### onProgressChanged: " + progress);
// update current position here
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
// Log.i("TAG", "#### onStartTrackingTouch");
// this tells the controller to stay visible while user scrubs
mController.show(3600000);
// if it is possible, pause the video
if (playerState == MPState.Started || playerState == MPState.Paused) {
mediaPlayer.pause();
playerState = MPState.Paused;
}
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
int seekValue = seekBar.getProgress();
int newMinutes = Math.round((float)getTotalDuration() * (float)seekValue / (float)seekBar.getMax());
// Log.i("TAG", "#### onStopTrackingTouch: " + newMinutes);
mController.show(3000); // = sDefaultTimeout, hide in 3 seconds
}
});
Caveats:
This is not updating the current position as you scrub, the left-hand-side TextView time value. (I had a truly remarkable way to do this which this margin is too small to contain).
Related
Below is the code of the seekbar for my audio player. Currently, I am able to move it along, but once I lift my finger, it returns to the original song duration again. How can I navigate to any time of the song through the seekbar?
//initialize seekbar
seekbar = (SeekBar)findViewById(R.id.seekBar1);
seekbar.setClickable(false); //this should be set to true, correct?
Below is where seekbar is involved, in play button onclick
public void play(View view){
mediaPlayer.start();
finalTime = mediaPlayer.getDuration();
startTime = mediaPlayer.getCurrentPosition();
if(oneTimeOnly == 0){
seekbar.setMax((int) finalTime);
oneTimeOnly = 1;
}
seekbar.setProgress((int)startTime);
}
And it is also involved in UpdateSongTime method
private Runnable UpdateSongTime = new Runnable() {
public void run() {
startTime = mediaPlayer.getCurrentPosition();
startTimeField.setText(String.format("%d:%02d",
TimeUnit.MILLISECONDS.toMinutes((long) startTime),
TimeUnit.MILLISECONDS.toSeconds((long) startTime) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.
toMinutes((long) startTime)))
);
seekbar.setProgress((int)startTime);
myHandler.postDelayed(this, 100);
}
};
Implement SeekBar.OnSeekBarChangeListener. This interface contains three callbacks you can use for managing your audio player:
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// MediaPlayer.seekTo(progress)
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
// MediaPlayer.pause()
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
// MediaPlayer.seekTo(seekBar.getProgress())
}
In addition, you may want to have your UpdateSongRunnable pause its updates between onStartTrackingTouch() and onStopTrackingTouch(), otherwise it will continue to try to update while you have your finger on the seekbar.
You need to implement the OnSeekBarChangeListener. When you perform certain acts with the SeekBar, these interface's methods are invoked. For example you say you want to move the track playback to a certain time according to where your end-user lifts up her finger. Override the onStartTrackingTouch (SeekBar seekBar) to (if you wish) pause the music and the onStopTrackingTouch (SeekBar seekBar) to start the music again at the time you desire.
Note: The seek bar is passed as a parameter in order for you to check at what time did the user lift her finger up.
Hi there. As you can see i want to show buffered level of media that stream to user in android music player.The solution i imagine for this issue is that:
Should i create a seekbar and a progressbar with transparent background and overlap progressbar to seek bar, whereas progreesbar showing buffered level of music.
but im not sure about this strategy. is anyone to help me for best way to reaching the goal at this issue.
First of all should implements OnBufferingUpdateListener to activity then set onBufferingUpdate ike this:
public void onBufferingUpdate(MediaPlayer arg0, int percent) {
/** Method which updates the SeekBar secondary progress by current song loading from URL position*/
seekBarProgress.setSecondaryProgress(percent);
}
Just because setting percentage directly doesn't work. Here is the code that worked for me.
mediaPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
#Override
public void onBufferingUpdate(MediaPlayer mediaPlayer, int percent) {
double per = percent * 1.00;
per = (per / 100);
double f = (seekBar.getMax() * 1.00);
f = f * per;
if (percent < binder.seekBar.getMax()) {
seekBar.setSecondaryProgress((int) f);
}
}
});
These int to double conversions are necessary because int division by 100 will give you 0 if result was to be 0.12 or 0.xx.
i create a audio player and wanna play a audio several times but once it complete 1st time, the progress bar get stuck at the end.i tried to get the last position of progress bar by
boolean atEnd = audioStreamer.getMediaPlayer().getDuration()
- audioStreamer.getMediaPlayer().getCurrentPosition() == 0;
but it gives false every time.
thanks in advance.
You could simply set the OnCompletionListener[1].
audioStreamer.getMediaPlayer().
setOnCompletionListener(
new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
Log.d("MediaPlayer", "end reached");
}
}
);
[1]
Register a callback to be invoked when the end of a media source has
been reached during playback.
The MediaPlayer's getDuration() method is giving me an incorrect value for some audio files. I think the common trait for all these files is that they were manipulated using Audacity or some other audio editing tool. This is a problem when trying to tie MediaPlayer progress to a Progress Bar.
I went ahead and logged it:
while(mPlayer.isPlaying())
Log.i("progress/total",
mPlayer.getCurrentPosition() +
"/" + mPlayer.getDuration());
and found this:
I/progress/total(643): 14615/14620
I/progress/total(643): 14647/14620
This is only two log line of thousands, but the point is after the progress passes what getDuration() believes to be the total duration of the song, it just keeps going. Because the MediaPlayer can in fact give the correct total for duration, is there a way to use this to get a proper maximum for my ProgressBar?
I had similar problem when MediaPlayer.getDuration() returned 542434 ms for mp3 file (HTC Desire C with ICS 4.0.3).
File itself was around 89 seconds, difference is too big.
I checked mp3 file content and saw some strange xml like:
<?xpacket begin="п»ї" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.2.2-c063 53.351735, 2008/07/22-18:04:26 ">
After saving this file as new one that xml was dropped and getDuration() returned correct value.
I know that it will not help those who need playing files you can't modify, but for those who can - it should help.
I was trying to play demo player a few while ago ,when i tested in Android emulator ,its behavior was the same as like you mentioned in your question but when i tried in some real device it gave me accurate value of media duration.
If your intention is only play media syncing with seekbar then you can do something like below ,
if (!mediaPlayer.isPlaying())
mediaPlayer.start();
handler.post(new SeekbarRefresh(seekbar));
//Class to update progress of seekbar according to music player
private class SeekbarRefresh implements Runnable {
SeekBar seekBar;
public SeekbarRefresh(SeekBar seekBar, ImageView imageView) {
this.seekBar = seekBar;
}
#Override
public void run() {
if (mediaPlayer != null) {
if (mediaPlayer.getDuration() > 0) {
int currentDuration = mediaPlayer.getCurrentPosition();
seekBar.setProgress(currentDuration);
if (mediaPlayer.isPlaying()){
handler.post(this);
isAudioPlaying = true;
}
else {
handler.removeCallbacks(this);
isAudioPlaying = false;
}
}
}
}
}
seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
mediaPlayer.seekTo(progress);
}
});
I have encountered a similar situation. In my case the time difference between mPlayer.getDuration() to mPlayer.getCurrentPosition() was around 80 seconds.
After reading few posts on the subject, I used third party software to convert the mp3's sample rate from 22,000 kHz to 44,100 kHz. Once converted, the result of getDuration() and getCurrentPosition() are the almost the same (0.0012s constant error).
Here is the test used:
dur = mp.getDuration();
Log.d("dur", dur + " <- getDuration");
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
// finish current activity
Log.d("dur", mp.getCurrentPosition() + " <- getCurrentPostion");
}
});
Im developing one application which used SeekBar to control volume in
MediaPlayer. The problem is when i move the seek bar at that time only the
volume is increased, rest of
the time default volume was set. Here is my code
seek.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser)
{
//Log.i("Seek Bar", "" + validBpm);
mp.setVolume(left, right);
}
public void onStartTrackingTouch(SeekBar seekBar)
{
left=left+20;
right=right+20;
mp.setVolume(left, right);
}
public void onStopTrackingTouch(SeekBar seekBar) {
left=left-20;
right=right-20;
mp.setVolume(left, right);
}
});
Looks like a simple fix although your original questions verbiage is really difficult to understand.
You're trying to use your own formula to set the volume and it doesn't look like those 2 methods are a good place to do it. Those two methods should only fire once, since they are only a startTouch or finishTouch.
You need to be using the "progress" integer provided from the onProgressChanged method to set the volume to the mediaplayer accordingly. This integer is the value of where the user set the seekBar.