I am working on an app with recorder and player. I am using mediaplayer to play the recorded .wav file and meantime I want to update to a seekbar. Everything is working fine But my problem is mediaplayer progress updation to seekbar is not happening smoothly, If we are playig a small file, thumb of the seekbar jumps in seconds or between.
Can anyone help me with a workaround to make it smooth seeking of the progress in seekbar. My code is shown below.I am totlay stuck here.
mediaPlayerIntiate();
mediaPlayerSetSource();
mMediaPlayer.start();
task = new TimerTask() {
#Override
public void run() {
Graphbar.post(new Runnable() {
#Override
public void run() {
if (mMediaPlayer != null) {
if (playButtonState == MediaMode.PLAY) {
if (mMediaPlayer.isPlaying()) {
Graphbar.setProgress(mMediaPlayer
.getCurrentPosition());
mediaPlayerUpdateTimer(mMediaPlayer
.getCurrentPosition());
enableRewindandForward();
}
}
}
}
});
}
};
timer = new Timer();
timer.schedule(task, 0, 8);
mMediaPlayer.getCurrentPosition() Return current Time in millisecond and you are updating this to Seekbar which maximum capacity is 100. Make one formula to with length of file and 100. try this function
MediaPlayer mMediaPlayer = new MediaPlayer();
final SeekBar mSeelBar = new SeekBar(this);
final int duration = mMediaPlayer.getDuration();
final int amoungToupdate = duration / 100;
Timer mTimer = new Timer();
mTimer.schedule(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (!(amoungToupdate * mSeelBar.getProgress() >= duration)) {
int p = mSeelBar.getProgress();
p += 1;
mSeelBar.setProgress(p);
}
}
});
};
}, amoungToupdate);
And this process should be called when Media player start playing. inside
mediaPlayer.setOnPreparedListener(new OnPreparedListener(){
#Override
public void onPrepared(MediaPlayer mp) {
**// call here**
}
});
Update
Update 125 times in seconds is not something you should do. Please increase your interval for updating SeekBar. I adding this after reading comments of NullPointer
seekbar.setProgress() only accepts int. Hence, most of us tend to pass the elapsed percentage to this method. However, if you need much smoother progression, then you can use the duration in milliseconds as the MAX. Then we get to update the progression of the seekbar every millisecond. Below is an example and I have updated it every 15th millisecond as almost every android phone comes with a refresh rate of 60 fps (frames per second).
try{
mediaPlayer.start();
seekbar.setProgress(0);
seekbar.setMax(mediaPlayer.getDuration());
// Updating progress bar
seekHandler.postDelayed(updateSeekBar, 15);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
}
/**
* Background Runnable thread
* */
private Runnable updateSeekBar = new Runnable() {
public void run() {
long totalDuration = mediaPlayer.getDuration();
long currentDuration = mediaPlayer.getCurrentPosition();
// Displaying Total Duration time
remaining.setText(""+ milliSecondsToTimer(totalDuration-currentDuration));
// Displaying time completed playing
elapsed.setText(""+ milliSecondsToTimer(currentDuration));
// Updating progress bar
seekbar.setProgress((int)currentDuration);
// Call this thread again after 15 milliseconds => ~ 1000/60fps
seekHandler.postDelayed(this, 15);
}
};
/**
* Function to convert milliseconds time to
* Timer Format
* Hours:Minutes:Seconds
* */
public String milliSecondsToTimer(long milliseconds){
String finalTimerString = "";
String secondsString = "";
// Convert total duration into time
int hours = (int)( milliseconds / (1000*60*60));
int minutes = (int)(milliseconds % (1000*60*60)) / (1000*60);
int seconds = (int) ((milliseconds % (1000*60*60)) % (1000*60) / 1000);
// Add hours if there
if(hours > 0){
finalTimerString = hours + ":";
}
// Prepending 0 to seconds if it is one digit
if(seconds < 10) {
secondsString = "0" + seconds;
}else {
secondsString = "" + seconds;
}
finalTimerString = finalTimerString + minutes + ":" + secondsString;
// return timer string
return finalTimerString;
}
Here is how i handle the seekbar;
mediaPlayer.setOnPreparedListener(new OnPreparedListener(){
#Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer.start();
new SeekBarHandler().execute();
});
Now i have an Async Task called SeekBarHandler that handles the seekbar like this:
public class SeekBarHandler extends AsyncTask<Void, Void, Void> {
#Override
protected void onPostExecute(Void result) {
Log.d("##########Seek Bar Handler ################","###################Destroyed##################");
super.onPostExecute(result);
}
#Override
protected void onProgressUpdate(Void... values) {
seekBar.setProgress(mediaPlayer.getCurrentPosition());
super.onProgressUpdate(values);
}
#Override
protected Void doInBackground(Void... arg0) {
while(mediaPlayer.isPlaying()&&isViewOn==true) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
onProgressUpdate();
}
return null;
}
}
Now in my onPause, i terminate the AsyncTask as it doesnt make sense to keep the thread going when the user is not able to see the seekbar
protected void onPause() {
isViewOn=false;
super.onPause();
}
And on onResume i start the AsyncTaskAgain like this
protected void onResume() {
isViewOn=true;
new SeekBarHandler().execute();
super.onResume();
}
As you can see i use a boolean flag isViewOn to check whether the view is on or not to handle the seekbar.
player.prepare(); // or start()
ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
service.scheduleWithFixedDelay(new Runnable()
{
#Override
public void run()
{
progressBar.setProgress(player.getCurrentPosition());
}
}, 1, 1, TimeUnit.MICROSECONDS);
The problem you're experiencing has to do with the way Android's SeekBar is designed/implemented. While it functions very well, you're limited by a combination of segments used (i.e. seekbar.setMax(int)) and the delay time of your Handler.
That being said, I subclassed SeekBar to make my own SmoothSeekBar that uses ViewPropertyAnimators instead of a Handler.
Check it out here: https://github.com/Indatus/Android-SmoothSeekBar
private void startPlaying() {
mediaPlayer = new MediaPlayer();
try {
mediaPlayer.reset();
mediaPlayer.setDataSource(audioPlayerName);
mediaPlayer.prepare();
mediaPlayer.start();
setAudioProgress(); //call method
} catch (Exception e) {
e.printStackTrace();
}
}
public void setAudioProgress() {
total_duration = mediaPlayer.getDuration();
binding.total.setText(timeConversion((long) total_duration));
binding.current.setText(timeConversion((long) current_pos));
binding.seekbar.setMax((int) total_duration);
runnable = new Runnable() {
#Override
public void run() {
try {
current_pos = mediaPlayer.getCurrentPosition();
binding.current.setText(timeConversion((long) current_pos));
binding.seekbar.setProgress((int) current_pos);
handlerProgressBar.postDelayed(this, 1000);
Log.e(LOG_TAG, "11111");
} catch (IllegalStateException ed) {
ed.printStackTrace();
}
}
};
handlerProgressBar.postDelayed(runnable, 1000);
}
Related
I have audio files on my mysql server. When audio is played seekbar mismatch with current position and repeat audio in background with difference of milliseconds. When I remove seekbar.setMax(mPlayer.getDuration) it works fine. And in Android:7 audio not playing properly. Please help me to solve this problem as soon as possible. Thank You in Advance.
initilizeUI();
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
if (b && mPlayer.isPlaying()) {
mPlayer.seekTo((mediaFileLengthInMilliseconds / 100) * i);
Log.d("time", String.valueOf((mediaFileLengthInMilliseconds / 100) * seekBar.getProgress()));
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
return root;
}
#SuppressLint("ClickableViewAccessibility")
private void initilizeUI() {
btn.setOnClickListener((View.OnClickListener) this);
seekBar.setMax(99);
mPlayer = new MediaPlayer();
mPlayer.setOnBufferingUpdateListener(this);
mPlayer.setOnCompletionListener(this);
}
#Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
seekBar.setSecondaryProgress(percent);
}
#Override
public void onCompletion(MediaPlayer mp) {
if (getActivity() != null) {
int id = getResources().getIdentifier(String.valueOf(R.drawable.restartbtn), "drawable", getActivity().getPackageName());
btn.setImageResource(id);
}
}
#Override
public void onClick(View v) {
if (v.getId() == R.id.imageButton) {
try {
mPlayer.setDataSource(audio_url);
mPlayer.prepare(); // you must call this method after setup
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
FFmpegMediaMetadataRetriever mmr = new FFmpegMediaMetadataRetriever();
mmr.setDataSource(audio_url);
mediaFileLengthInMilliseconds = Integer.parseInt(mmr.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_DURATION));
} catch (Exception e) {
e.printStackTrace();
}
if (!mPlayer.isPlaying()) {
mPlayer.start();
btn.setImageResource(R.drawable.pausebtn);
} else {
mPlayer.pause();
btn.setImageResource(R.drawable.playbtn);
}
seekUpdation();
}
}
Runnable run = new Runnable() {
#Override
public void run() {
seekUpdation();
}
};
public void seekUpdation() {
if (mPlayer.isPlaying()) {
seekBar.setProgress((int) (((float) mPlayer
.getCurrentPosition() / mediaFileLengthInMilliseconds) * 100));
seekHandler.postDelayed(run, 1000);
}
}
#Override
public void onDestroy() {
super.onDestroy();
if (mPlayer.isPlaying()) {
mPlayer.stop();
}
}
I've checked your MP3 file and yes it is encoded in Variable Bit-Rate mode.
Possible solutions:
(1) Confirm if working: The CBR version of your MP3 (right-click and save, then put & test on your server). If seekbar is working as expected then your solution is to re-convert. You can use a tool to re-convert MP3 from VBR mode into CBR mode.
Try converting via this site : https://audio.online-convert.com/convert-to-mp3
If converting many files try : FFmpeg which runs on the commandline, can process multiple files in a folder.
(2) Your values from .getDuration() and .getCurrentPosition() must be divided by 1000.
.getDuration() will return a number in milliseconds. It takes 1000 m-secs to make 1 second. So your code must get expected value by dividing. eg: 5000 ms / 1000 = 5 seconds duration.
examples:
//# when using "getDuration"...
seekBar.setMax( mPlayer.getDuration() / 1000 );
//# when using "getCurrentPosition"...
seekBar.setProgress( mPlayer.getCurrentPosition() / 1000 );
I am developing a new Android application, In my application, i have audio record functionality of 3 sec. I want to show a round progress bar to show the progress of recording. The recording automatically stop after 3 sec. This is my code, Please help me to add the progress bar.
private void mStartRecordingAudio() {
try {
TemporaryModelCache.stopMediaPlayer();
TemporaryModelCache.getAsyncInstance().cancel(true);
TemporaryModelCache.clearAsyncInstance();
mAudioRecorder = new MediaRecorder();
mAudioRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
mAudioRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mAudioRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
String file_name = mFileToSaveAudio + "recording.3gp";
mAudioRecorder.setOutputFile(file_name);
mAudioRecorder.prepare();
mAudioRecorder.start();
mImageViewRecord.setImageResource(R.drawable.ic_button_recording);
circularProgressBar.setVisibility(View.VISIBLE);
mImagePlayOurs.setImageResource(R.drawable.ic_button_play_ours_disabled);
mButtonPlayRecordedAudio.setImageResource(R.drawable.button_yours_disabled);
mViewRecord.setEnabled(false);
mViewPlayYours.setEnabled(false);
mViewPlayOurs.setEnabled(false);
} catch (IllegalStateException ise) {
Log.e("error", ise.toString());
// make something ...
} catch (IOException ioe) {
Log.e("error", ioe.toString());
// make something
}
new Timer().schedule(new TimerTask() {
#Override
public void run() {
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
try {
mAudioRecorder.stop();
mAudioRecorder.reset();
mAudioRecorder.release();
} catch (RuntimeException stopException) {
//handle cleanup here
}
circularProgressBar.setVisibility(View.GONE);
mWowWordActionSounds.mPlayAudioButtonClick();
mIsAudioRecorded = true;
mButtonPlayRecordedAudio.setImageResource(R.drawable.ic_button_yours_png);
mImagePlayOurs.setImageResource(R.drawable.ic_btn_playours);
mImageViewRecord.setImageResource(R.drawable.ic_btn_record);
mViewPlayYours.setEnabled(true);
mViewPlayOurs.setEnabled(true);
mViewRecord.setEnabled(true);
}
});
}
}, 3000);
}
You are not updating the progress bar anywhere in the code
You should be using circularProgressBar.setProgress(circularProgressBar.getProgress()+1000); inside run().
But in my opinion, you should be using a Countdown Timer class instead of scheduling with Timer.schedule().
A simple example of CountDownTimer that will do a task inside onTick() after every 1 second and call onFinish() after 3 second
You can place the code for setProgress inside onTick().
new CountDownTimer(30000, 1000) {
public void onTick(long millisUntilFinished) {
mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
}
public void onFinish() {
mTextField.setText("done!");
}
}.start();
Read more at: https://developer.android.com/reference/android/os/CountDownTimer
I am using IVideon Player and I have an URL which I want to play. Things are okay but I am unable to handle Seekbar status. I am getting 0 when I want to get time how much my video has been played. Please help me out. What I did is here
class SeekBarProgress implements Runnable {
#Override
public void run() {
int totalDuration = event_list.get(counter).getDuration();
for (int i = 0; i <= totalDuration; i++) {
if (isPlaying) {
final int value = i;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mTimeUpdateHandler.post(new Runnable() {
#Override
public void run() {
strttime.setText(Utils.getDuration(Utils.getLong((double) value))); // the TextView Reference
mBufferBar.setProgress(value);
}
});
}
}
}
}
I am trying to update seekbar with respect to the progress of the song in MediaPlayer.
I am using Thread to do that task.
First i have used Thred inside thread and trying to update the UI but it crashing the app and says that only original thread can attached to the view.
Then i have try to update it with handler inside the thread runnable. which works fine but it is not updating the seekbar. When i have do log then i come to know loop is not going inside my handler. I dont know where is the problem. Please help me to updating SeekBar.
Code:
private Runnable mUpdateTimeTask = new Runnable() {
public void run() {
try {
if ((musicPath != mIRemoteService.getPath())) {
System.out.println("..... MUSIC CHANGE.....");
setOrUpdateData();
updateFragmentLayout();
}
// Displaying Current Duration time/Progress
new Handler().post(new Runnable() {
#Override
public void run() {
try {
songCurrentDurationLabel.setText(""+ Utilities.milliSecondsToTimer(mIRemoteService.position()));
songProgressBar.setProgress((int)mIRemoteService.position());
System.out.println("Runnnnn.........");
//songProgressBar.invalidate();
} catch (RemoteException e) {
e.printStackTrace();
System.out.println("Runnnnn......... EXCEPTION....");
}
}
});
System.out.println("Runnnnn.........MAIN....");
if (!(mIRemoteService.isPlayerRunning())) {
btnPlay.setImageDrawable(getResources().getDrawable(R.drawable.play_now_playing));
mHandler.removeCallbacks(mUpdateTimeTask);
System.out.println("Runnnnn.........MAIN....IF");
}else{
System.out.println("Runnnnn.........MAIN....ELSE");
mHandler.post(mUpdateTimeTask);
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
you can use either a handler or a thread, with thread you should make sure to post the modifications on the UI thread:
private class UpdateSeekBar extends Thread
{
#Override
public void run()
{
super.run();
while (null != mp && mp.isPlaying() && this.isAlive())
{
//final int min = (mp.getCurrentPosition() / 1000) / 60;
//final int sec = (mp.getCurrentPosition() / 1000) % 60;
MainActivity.this.runOnUiThread(new Runnable()
{
#Override
public void run()
{
try
{
songCurrentDurationLabel.setText("" + Utilities.milliSecondsToTimer(mIRemoteService.position()));
songProgressBar.setProgress((int) mIRemoteService.position());
System.out.println("Runnnnn.........");
// songProgressBar.invalidate();
}
catch (RemoteException e)
{
e.printStackTrace();
System.out.println("Runnnnn......... EXCEPTION....");
}
}
});
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
I am developing an application in which I used VideoView to play a video. What I want is I need to display some text underneath the playing video and the text should be changed as the video plays I mean depending on elapsed time. Like SRT. So, How to get elapsed time of video in android? And when we pause the video according text should be paused as well and after that when we resume video the text and the following text should be displayed.
Any help would be appreciated.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.playing);
mVideoView = (VideoView)findViewById(R.id.VideoView);
uri = Uri.parse("android.resource://com.abhan.video/" + R.raw.abhan);
Date dt = new Date();
mHours = dt.getHours();
mMinutes = dt.getMinutes();
mSeconds = dt.getSeconds();
String curTime = mHours + ":"+ mMinutes + ":" + mSeconds;
mVideoView.setVideoURI(uri);
mVideoView.start();
Runnable runnable = new CountDownRunner();
myThread= new Thread(runnable);
myThread.start();
mVideoView.setOnPreparedListener(new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
Log.i("TAG", "On Prepared");
}
});
mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
Log.v("TAG", "On Completion");
myThread.stop();
Intent i = new Intent(Playing.this, VideoPlay.class);
startActivity(i);
finish();
}
});
}
class CountDownRunner implements Runnable {
public void run() {
while(!Thread.currentThread().isInterrupted()) {
try {
doWork();
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
} catch(Exception e) {
e.printStackTrace();
}
}
}
}
public void doWork() {
runOnUiThread(new Runnable() {
public void run() {
try {
mText = (TextView)findViewById(R.id.SetText);
Date dt = new Date();
int hours = dt.getHours();
int minutes = dt.getMinutes();
int seconds = dt.getSeconds();
String curTime = hours + ":"+ minutes + ":" + seconds;
if(minutes == mMinutes && seconds == mSeconds) {
mText.setText(getString(R.string.one));
} else if(minutes == mMinutes && seconds == mSeconds+20) {
mText.setText(getString(R.string.two));
} else if(minutes == mMinutes && seconds == mSeconds+38) {
mText.setText(getString(R.string.three));
} else if(minutes == mMinutes && seconds == mSeconds+47) {
mText.setText(getString(R.string.four));
} else if(minutes == mMinutes+1 && seconds == mSeconds2+2) {
mText.setText(getString(R.string.five));
} else if(minutes == mMinutes+1 && seconds == mSeconds2+22) {
mText.setText(getString(R.string.six));
} else if(minutes == mMinutes+2) {
mText.setText(getString(R.string.seven));
} else if(minutes == mMinutes+2 && seconds == mSeconds2+2) {
mText.setText("");
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((!(android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.DONUT)
&&keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0))
{
onBackPressed();
}
return super.onKeyDown(keyCode, event);
}
public void onBackPressed() {
Intent intent = new Intent(Playing.this, VideoPlay.class);
intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(intent);
finish();
return;
}
Thanks.
Isn't it VideoView's getCurrentPosition() what you are looking for?
To change the contents of your TextView (or whatever yo want to use), I would set a Timer, with enough frecuency to update your "subtitle". Its TimerTask could get the playback time with that getCurrentPosition(), and use a Map to store messages values and the time as the key.
Here it's and example of what I'm thinking:
00 - "Video begins!"
05 - "something funny happens"
12 - "Video ends!"
class MySubtitlePoster extends TimerTask{
private VideoView video;
private TreeMap <Integer, String> messages; // populate it somewhere
public MySubtitlePoster(VideoView v) {
video = v;
}
public void run() {
int videoPos = video.getCurrentPosition();
String messageToDisplay = messages.floorKey(new Integer(videoPos));
// If all this is right, now you can get the message and post it, probably using a Handler
}
}
==========================================
After seeing your complete code, I can give you more detailed tips, but the coding thing is your job, so...
To create the map:
messages = new TreeMap();
messages.put(new Integer(0), getString(R.string.one));
messages.put(new Integer(20), getString(R.string.two));
...
messages.put(new Integer(62), getString(R.string.four));
To do the work:
public void doWork(){
runOnUiThread(new Runnable() {
public void run() {
try{
mText = (TextView)findViewById(R.id.SetText);
//If it returns milliseconds, divide by 1000
int playTime = mVideoView.getCurrentPosition();
String textValue = messages.ceilingEntry(new Integer(playtime)).getValue();
mText.setText(textValue);
}catch (Exception e) {
e.printStackTrace();
}
}
});
}
And finally, use a Timer instead of this (There is an article here about Timers and UI):
public void run()
{
while(!Thread.currentThread().isInterrupted())
{
try
{
doWork();
Thread.sleep(1000);
}catch (InterruptedException e)
{
Thread.currentThread().interrupt();
e.printStackTrace();
}catch(Exception e)
{
e.printStackTrace();
}
}
}
It is really ugly and inefficient .
So, happy coding, and please if you find some inccurancy try to solve it yourself, not because I'm not kind on helping, but it's your best chance to improve your skills.
Regards, Manuel.