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.
Related
Im trying to get the seekbar to update and show progress whenever I play an mp3 with mediaPlayer. Music plays fine everytime, seekbar will always snap to 0 position when mp3 is playing.
I was trying to follow this answer but it just wont work... SeekBar and media player in android
I have this code in main activity
private Handler mHandler = new Handler();
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
if (Assets.mediaPlayer != null) {
int mCurrentPosition = Assets.mediaPlayer.getCurrentPosition() / 1000;
if(OnionFragment.seekBar!= null) {
OnionFragment.seekBar.setProgress(mCurrentPosition);
}
}
mHandler.postDelayed(this, 1000);
}
});
in OnionFragment I have
public static SeekBar seekBar;
seekBar = (SeekBar) rootView.findViewById(R.id.seekBar);
and in OnionFragments onclick (the play button)I have
Assets.playMusicFile(MainActivity.items.get(MainActivity.selectedItem).getSongId(), true);
if(Assets.mediaPlayer!=null) {
seekBar.setMax(Assets.mediaPlayer.getDuration());
}
OnionFragment is loaded into MainActivity right away and looks like this
P.S. If anyone has extra time, how do i change size of seekbar ball and color
Change
seekBar.setMax(Assets.mediaPlayer.getDuration());
to
seekBar.setMax(Assets.mediaPlayer.getDuration()/1000);
Using the Vitamio media player, I do not see a constant for when the video actually starts rendering (as there has been for the normal android MediaPlayer since api 17). onPreparedListeners do not detect when the rendering physically starts, and, as a result, the black screen prior to the video starting is seemingly unavoidable.
Is there any way to detect when the video has actually started rendering in Vitamio?
Though it's a bit of a hack, I found that this way works wonders:
Create a boolean, defaulted to false, which determines whether or not buffering has completed for the first time.
Set an onInfoListener on your Vitamio VideoView.
Look for MediaPlayer.MEDIA_INFO_BUFFERING_END
If the boolean you created is false, then set it to true and wait, with a while loop, until yourVideoView.getCurrentPosition() != 0. Note that 0 may be too low -- sometimes getCurrentPosition will return a number higher than zero before it has started, but typically the returned value will be no higher than 1000 / (the fps of your video). I used 40.
Execute desired code (remove other views to make the VideoView visible, or add the VideoView to the layout).
In an OnCompletionListener, set the created boolean back to false.
public class AnimationCanvas extends VideoView{
public static AnimationCanvas animationCanvas;
private static boolean bufferedOnce = false;
public AnimationCanvas(Context context, AttributeSet attrs){
super(context, attrs);
animationCanvas = this;
getHolder().addCallback(this);
this.setOnInfoListener(new MediaPlayer.OnInfoListener() {
#Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
if (!bufferedOnce && what == MediaPlayer.MEDIA_INFO_BUFFERING_END) {
bufferedOnce = true;
while (getCurrentPosition() < 40) {
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
MainActivity.frameLayout.removeView(MainCanvas.mainCanvas);
return true;
}
return false;
}
});
this.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
bufferedOnce = false;
MainActivity.frameLayout.addView(MainCanvas.mainCanvas);
MainActivity.frameLayout.removeView(animationCanvas);
}
});
}
EDIT: Note that another option is to create a separate Runnable which does the waiting (while(vv.getCurrentPosition() < 40){}) and then, in the same Runnable, call runOnUIThread() to run a second runnable which alters / removes / adds views if needed (Views can only be touched by the thread in which they were created.) This way, there is no need for an onInfoListener -- just start the first Runnable in an onPreparedListener.
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).
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");
}
});
I'm trying to implement a MediaPlayer on an Android app, but now I have two problems, which are not THE BIG SHOWSTOPPER but they are more then annoying and i have to fix it, just for me.
I implemented a async MediaPlayer+Controller to a Activity, which works fine.
My plan was to show also the percentage of the buffering on the MediaControl. This also works.
But now, after I can see the percentage, I saw a strange behaviour: if I seek to a position which is already in the buffer, the buffering will start from this position again. Is this a known and/or normal behavior/problem/feature ?
Here are more details:
I'm using the 2.2 SDK
This is how I implement it
public class Details extends Activity implements MediaPlayer.OnPreparedListener, MediaController.MediaPlayerControl {
[...]
private void setPosition(int currentPos ){
position = currentPos;
}
[...]
public void onCreate(Bundle savedInstanceState) {
[...]
mediaPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
#Override
public void onBufferingUpdate(MediaPlayer mp, int progress) {
setPosition(progress);
}
});
[...]
public int getBufferPercentage() {
return position;
}
[...]
public void seekTo(int i) {
General.mediaPlayer.seekTo(i);
}
}
What I expected after clicking on the seekbar
What I got
Is this normal?
This thread confirms that although a position is already buffered MediaPlayer sends a request to a server.