I am trying to Seek to a particular location in a video in Android and I am completely stuck because of the inconsistencies it is showing in its behaviour. Here's a list of things I ve done
VideoView.Seekto goes to 5:19 for one video, 5:17 for one video and 5:32 for another for the same milliseconds(326000 ms)!
Since VideoView does not support onSeekListener, I've modified the
source VideoView and added support for it. Yet it does not pause and
start seeking from where I want it to - there is always a lag! The
onSeekListener is called immediately some 6 s before where I want it
to stop. In many cases the seek bar shows the right time and suddenly jumps back a few seconds even though I am calling video.start
from onSeekCompleteListener
Why is this so inconsistent ? Is there a definite way of seeking to where you want to go in milliseconds in videoview? I know I should use MediaPLayer + Surface but since VideoView is just a wrapper of the two I am modifying it at source but to no avail.
I am aware of this : http://code.google.com/p/android/issues/detail?id=9135
But is there any way to get around this and have a definite way of
1.) Seeking to the exact time in milliseconds
2.) Pausing and resuming at the exact time?
You have to wait for the seeking to complete.
VideoView does not have a OnSeekCompleteListener() but you can access the MediaPlayer from the onPrepared method of the VideoView and then set the OnSeekCompleteListener, like this :
videoView.setOnPreparedListener(new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.setOnSeekCompleteListener(new OnSeekCompleteListener() {
#Override
public void onSeekComplete(MediaPlayer mp) {
//Seek completed. Move seekbar
}
});
}
});
10 years later and I came across the same question exactly.
Any way, for me, the solution was to use the mediaPlayer from inside VideoView (Android Oreo 8.0+):
Explanation:
videoView default seekTo function use mediaPlayer default seekTo function (source)
mediaPlayer default seekTo overload is the same as seekTo(long, int) with SEEK_PREVIOUS_SYNC mode, you should use SEEK_PREVIOUS_SYNC only if you want to seek to a sync frame that has a timestamp earlier than or the same as milliseconds given,
However, SEEK_CLOSEST will seek to a frame that may or may not be a sync frame but is closest to or the same as milliseconds.
I know this is not the proper solution to your question but take a look into this library that is being made over the Google's default video view
https://github.com/brianwernick/ExoMedia
It has all the functionality and more that is being supported by default video view.
You can use this video view and emVideoView.seekTo(1000); to jump to 1000 millisecond in the video. You can also have setOnSeekCompletionListener to do process when seek complete.
I solved this problem like this
the seekTo() function doesn't work in VideoView
Related
I researched a little bit, but couldn't find any solutions to this problem:
I would like to play a MediaPlayer and pause/stop it at a given time.. (ie: play from second 6 to second 17).
I know that I can set its starting point with seekTo() method, but can I pause/stop it from playing by setting an end point (of course, before reaching the file end limit)?
There are different ways you could do this, here's one:
int startFrom = 6000;
int endAt = 11000;
MediaPlayer mp;
Runnable stopPlayerTask = new Runnable(){
#Override
public void run() {
mp.pause();
}};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mp = MediaPlayer.create(this, R.raw.my_sound_file);
mp.seekTo(startFrom);
mp.start();
Handler handler = new Handler();
handler.postDelayed(stopPlayerTask, endAt);
}
The mediaplayer will start playing 6 seconds in and pause it 11 seconds after that (at second 17).
I think you can create Timer and call seekTo() directly from its task. Then call stop()/pause() inside of that Timer Task.
Maybe this post will be helpfull for you.
Or you can use handler for this task, like Ken Wolf shows you.
Best wishes.
You can use CountDownTimer
new CountDownTimer(30000, 1000) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
mp.stop;
mp.relese();
}
}.start();
I know this already has an answer but here is an alternative for anyone wanting to do this efficiently for a video with controls.
I saw a different answer that involved constantly checking the position.
Note that this assumes that you do not need a lot of different portions of one video. Even in that case I suggest to follow the below.
If you only need to play a portion of the video and want it to end somewhere, then why not just use free video editing software and clip the end? You can still start it from anywhere using seekTo() but this way you don't have to waste resources checking for a certain position (video with controls).
If you do not have video player controls the accepted answer will work. But if you do have player controls then it would not since a user could pause and play the video.
There's a time difference between videoView.start() and video prepared to play depend on video format, those time difference is probably more than 10 frames.
so the best way to do is to start the timer inside OnPreparedListener to minimise the time difference, or even more to get the current playing duration and set a postDelayed timer at that point.
I try to stream (progressive e.g: http://server.com/video.mp4)
when i use the standard google mediaplayer (VideoView from android package) and register an onBufferingUpdateListener then i get the bufferpercentage that refers to the download state of the hole video. This player has also a loading view where i can see the buffer state.
This bufferpercentage and view shows me how much of the video has been downloaded.
Now when i use the Vitamio player, the onBufferingUpdateListener shows me after a few seconds 99 percent of buffering and there is no loading view too. And when i pause the playback it stops buffering immediately instead of continue buffering like the google videoview does. This is very usefull if you have a slow http stream.
Is there a way to make the vitamio-videoplayer buffer the videofiles in the same way as the google videoplayer does?
thank you
daniel
Sorry i posted that question as wrong user. Here the Answer of what i tried:
VideoView (android default - just plays few video formats) from inside the android.widget and from io.vov.vitamio.widget (vitamio - plays most video formats) package has the same structure. In both you can register an OnBufferingUdateListener that returns the bufferstate in percent:
videoview.setOnBufferingUpdateListener(new io.vov.vitamio.MediaPlayer.OnBufferingUpdateListener() {
public void onBufferingUpdate(io.vov.vitamio.MediaPlayer mp, int i) {
Log.v(TAG, "Buffer percentage done: "+i);
}
});
or with the android default VideoView:
videoview.setOnBufferingUpdateListener(new android.media.MediaPlayer.OnBufferingUpdateListener() {
public void onBufferingUpdate(android.media.MediaPlayer mp, int i) {
Log.v(TAG, "Buffer percentage done: "+i);
}
});
If i use android.widget.VideoView the buffer percentage slowly increases until it reaches 100% - The video file has been downloaded completely. And it continues updating BufferingUpdate when i press the pause button.
When i use io.vov.vitamio.widget.VideoView the percentage reaches 100% within seconds. Then the video starts and the OnBufferingUpdateListener never gets called again (when i call getBufferPercentage it is always at 99 percent. That seems to be the reason). And as i sayed: It seems to stop buffering when i press the pause button.
I think the buffering works different in vitamio. But that's crap. Especially when i stream videos from the web and the video datarate is higher than the download speed i need to prebuffer the video by pressing pause and wait until it has downloaded enough data to watch it smoothly. Hope you got what i mean. thank you
I'm working on an app in which the video is paused at 3 different intervals. After the second pause, if a button is clicked, it should start back from the previous location.
Eg. if it is currently paused at 1:30, then on click of a button, it goes to the previous bookmark, i.e. 00:45.
I thought using MediaPlayer.seekTo() will help me achieve this. But, seekTo() doesn't seek the position at all. The currentPosition stays the same even after a call to seekTo();
Here's my code.
mediaPlayer.setOnSeekCompleteListener(new OnSeekCompleteListener() {
#Override
public void onSeekComplete(MediaPlayer mp) {
Log.d("VID_PLAYER","Seek Complete. Current Position: " + mp.getCurrentPosition());
mp.start();
}
});
and somewhere below, I have this...
mediaPlayer.seekTo(45000);
What is the problem? Why isn't seekTo(); working?
Any help would be appreciated.
I am currently testing it on Android v4.0.3 (ICS)
Try the code snippet given below to achieve more accuracy in seeking to specific positions of the media.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
mediaPlayer.seekTo(seekPosition,MediaPlayer.SEEK_CLOSEST);
else
mediaPlayer.seekTo((int)seekPosition);
Keep in mind that - as the accuracy increases, speed of seeking decreases. So while playing high resolution videos its advised not to use MediaPlayer.SEEK_CLOSESTmore often.
One of the reasons why Android is not able to do seekTo is because of strange encoding of the videos. For example in MP4 format so called "seek points" (i.e. search index) can be specified at the begining and at the end of the file. If they are specified at the begining of the file then seekTo will behave correctly. But if they are specified at the end of the file then seekTo will do nothing and video will start from the begining after seekTo.
This is confirmed bug-or-feature in Android 4.4.2.
P.S. On Youtube all videos are encoded with "seek points" at the begining of the file. So if you have this problem with seekTo in your app first of all check your app with some video files from Youtube. Perhaps you'll need to reencode your videos...
Does your problem have something to do with this bug:
https://code.google.com/p/android/issues/detail?id=4124
I recall encountering this about a year ago. I don't think I found a workaround at the time.
Since API 26, Android added a
seekTo(long msec, int mode);
By specifying a mode, we are able to tell to our MediaPlayer how to seek:
SEEK_PREVIOUS_SYNC: Has the same behavior as seekTo(int msec). It looks for the nearest 'seek point' (i.e. sync frame) backwards.
SEEK_NEXT_SYNC: Same as PREVIOUS, but looks forwards.
SEEK_CLOSEST_SYNC: This looks for the nearest sync frame given a msec time.
SEEK_CLOSEST: This seeks for the nearest frame given a msec time.
I faced a similar problem a couple days ago and by using SEEK_CLOSEST mode, this problem was solved.
For reference, check out these links: https://developer.android.com/reference/android/media/MediaPlayer.html#seekTo(long,%20int)
https://en.wikipedia.org/wiki/Video_compression_picture_types
App I'm developing contains many short (1-2 sec) videos.
The videos are displayed in one activity. User can either replay video (possibly while video is beeing played) or change actual video.
Part of code changing video:
String videoPath = getVideoPath();
videoView.setVideoPath(videoPath);
videoView.start();
Those 3 lines already causes app to load new video and play it.
Problem starts after video is completed. From this point loading new video causes many problems (Like sometimes for half a movie only sound is played while screen is black blank). There are similar problems with replaying video (which I end up with calling 3 lanes from above).
It seems like android after completing movie releases resources or something like this (and that's why I am settings same path, when I want to replay video).
Ideally I would want video to simply pause and seekTo to beggining of movie after finished playing (but I cannot do this in OnCompletedListener, since it already changed state to stopped...).
Can I somehow achieve this? (By this I mean -> after completed video pauses and seekTo to beginning)
I already tried all combinations of pausing vidoes, suspending them, setting OnPreparedListener, setting OnCompletedListener.
Thx!
Try something like
mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer arg0) {
mVideoView.start();
}
});
mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
mp.reset();
mVideoView.setVideoPath(file.getAbsolutePath());
mVideoView.start();
}
});
stop the playback, change video path, start video
videoView.stopPlayback();
videoView.setVideoPath(newVideoPath);
videoView.start();
I refactored my code in a following way: Everytime I need new video I reload whole activity. It works most of times, but now instead of video blackness at the beginning of playing I sometimes get "Cannot open media file" error.
Has it something to do with android resource managment? I release mediaPlayer in onCompletionListener.
Has anyone had such problem with playing many videos from external storage?
You can do something like this:
videoView.setOnCompletionListener(MediaPlayer.OnCompletionListener { mp ->
mp.seekTo(0);//go to second 0
mp.start()// start again
})
I discover in this link:
https://developer.android.com/reference/android/media/MediaPlayer
Do this.
videoView.setOnPreparedListener { mp ->
mp.isLooping = true
)
I've got to be missing something obvious here, but I can't seem to find anything to allow me to determine when MediaPlayer is buffering audio. I'm streaming internet audio and I want to display a buffering indicator, but nothing I've tried allows me to know when MediaPlayer interrupts the audio to buffer, so I can't properly display a buffering indicator. Any clues?
Like below (API level ≥ 9):
mp.setOnInfoListener(new OnInfoListener() {
#Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
switch (what) {
case MediaPlayer.MEDIA_INFO_BUFFERING_START:
loadingDialog.setVisibility(View.VISIBLE);
break;
case MediaPlayer.MEDIA_INFO_BUFFERING_END:
loadingDialog.setVisibility(View.GONE);
break;
}
return false;
}
});
NOTE : There is a known bug in Android. When playing HLS stream it's just never calls OnInfoListener or OnBuffering. check this link OnInfoListener bug
Ok, I feel a little vindicated now. I checked out the Pandora app and it doesn't display a buffering indicator. When music is interrupted for buffering, it just sits there as if nothing happened and the UI looks like it's still playing. So I've come to the conclusion that if you're using MediaPlayer, it's just not possible to determine if the track is temporarily paused for buffering.
However, I did notice that there are a couple MediaPlayer constants that could be of use:
MEDIA_INFO_BUFFERING_START and MEDIA_INFO_BUFFERING_END. But they're only available in API level 9+, and the docs don't say anything about them. I'm assuming they can be used with an OnInfoListener.
I'm disappointed, but at least I can stop spinning my wheels now and move on to something else.
#Daniel, per your comment on #JRL's answer, you could probably get this working by spinning up a thread and waiting for a timeout.
Something like DetectBufferTimeout.java (untested) would do nicely.
I do, however, agree that spinning up this separate thread is a bit of a hack. Perhaps OnBufferingUpdateListener could make a guarantee as to how often it calls onBufferingUpdate() regardless of whether a change in the buffering progress has occurred so we can detect if we're getting the same value over and over.
Register an OnBufferingUpdate listener.
You can use a thread that checks the current position of the MediaPlayer. If the position doesnt change, you can conclude that the media is in buffering state. Here is the complete tutorial that i wrote: http://www.ottodroid.net/?p=260
Just like #JRL said, register a OnBufferUpdateListener but register it on the MediaPlayer object in OnPreparedListener, that way anytime the music is buffering it'll always indicate. as this listener is always called when mediaplayer is buffering. like so:
player.setOnPreparedListener(mediaPlayer -> {
mediaPlayer.setOnBufferingUpdateListener((mediaPlayer1, percent) -> {
if (percent<=99)view.showMusicBuffer();
else view.hideMusicBuffer();
});
view.setTrackDuration(mediaPlayer.getDuration());
mediaPlayer.start();
changeTrackBarProgress();
});