Video view seeking inconsistencies - android

I have a video that I record at 60 frames per second or 30 frames per second. Now I view that video in a video view. I want the video to skip frame by frame on each skip button tap.
The issue is that the seek to method looks for nearest key frame and move to that position. So sometimes when I am skipping frames it shows inconsistent behaviour due to that.
So how can I achieve this?
This is the code I am using to seek the video view:
int currentPosition = videoView.getCurrentPosition();
double increment = 1000/60;
currentPosition += Math.round(increment);
if (videoLoader.canSeekForward()) {
videoLoader.seekTo(currentPosition);
}
So the method videoView.getCurrentPosition() sometimes returns wrong value.

I solved this issue by replacing video view with ExoPlayer. ExoPlayer has a method called setSeekParameters. Just use that method with the below parameter:
setSeekParameters(SeekParameters.EXACT)
and then the seekto method will seek to exact frames and not just the nearest key frames.

Related

How to get current frame number from video in android?

Is there a way to get the current frame number of the video while video is playing, or when the video has paused? using videoview.
With the VideoWiew, you can retrieve the playback time in milliseconds with getCurrentPosition. Then you have to make some calculations based on the frame rate of the video itself, for which I invite you to read here.
You should be able to get it by calling getCurrentPosition() on your videoView. Such as(videoView.getCurrentPosition). and place that inside a pause button or something along those lines.
getCurrentPosition() – Returns an integer value indicating the current position of playback.
Source: https://www.techotopia.com/index.php/Kotlin_Android_Video_Playback_using_the_VideoView_and_MediaController_Classes

exoplayer2: pause video at start with ConcatenatingMediaSource

While playing videos from a ConcatenatingMediaSource playlist, I would like the player to pause automatically at the start of a new item eg. not automatically playing it.
Using the demo application, I modified onPositionDiscontinuity function to detect current item change:
int currentPosition = 0;
#Override
public void onPositionDiscontinuity(#Player.DiscontinuityReason int reason) {
if (inErrorState) {
// This will only occur if the user has performed a seek whilst in the error state. Update
// the resume position so that if the user then retries, playback will resume from the
// position to which they seeked.
updateResumePosition();
}
if (player.getCurrentWindowIndex() != currentPosition) {
currentPosition = player.getCurrentWindowIndex();
player.setPlayWhenReady(false);
}
}
While this code pauses the player, it does not clear the surface view used by the player hence we are still seeing the last frame of the previous video. I suppose this callback is invoked too soon, but that's the only callback I found which was always invoked on playlist item change (onPlayerStateChanged might not be invoked).
How can I have the first frame of the newly current item displayed instead of the previous item last frame?
My weak workaround is to delay invocation of 200ms with Handler().postDelayed({ mPlayer?.playWhenReady = false }, 200).
This is not supported yet:
The problem with using the event listener is that there is no way to ensure the request to pause in your event listener
is handled while the first frame of the new playlist item is showing.
If stopping at approximately the right frame is fine, your solution of
pausing the player on position discontinuity looks fine, but I think
there's no guarantee that the video renderer will have kept up with
the player position which is why you still see a frame from the
previous source at the point of pausing the player. For what it's
worth, in my testing the video renderer did advance to the first frame
of the next playlist item before the request to pause was handled.
A couple of other suggestions:
You could try customizing your MediaSource to insert a position discontinuity at the start of each period. I think then you'd get
onRenderedFirstFrame at the start of each item. If you pause the
player there you can guarantee to pause the player at a frame in the
new playlist item.
To get this to work perfectly, in the sense that the first frame of the new playlist item is shown and the player pauses before any
other frame is shown, it will be necessary to coordinate showing a
frame and blocking further rendering (on the playback thread) with
pausing the player (on the thread your app is using to interact with
the payer). This will require a bit of code, but I think it is
probably possible roughly as follows: subclass MediaCodecVideoRenderer
and override onStreamChanged to get the time offset of the new
playlist item. Then override processOutputBuffer. In this method you
can detect when the first frame of the new stream has been rendered
(based on the time offset) and then prevent processing any output
frames until you've paused the player on your app's thread. After the
request to pause has been handled and the renderer is stopped
(onStopped) you can unblock processOutputBuffer.
We could also look at supporting this directly in the player, but it
will likely be a low priority at least for the moment.

Android: ExoPlayer - Get current frame number from video

I'm developing an Android video app where I need to get the current frame number of the video being displayed while in pause mode.
I need to send my Server the frame number currently paused in video and get back a list of items regarding that frame/time, right now I'm sending the current paused time in milliseconds, but it doesn't work quite well, because the Server compare the time sent to a specific frame it calculated, based on the time, but sometimes the comparison is not exact.
I know you can get a bitmap from that frame if you use MediaMetaDataRetriever, and I did it but it returns bitmap image and what I need is an index.
I'm using ExoPlayer (I need that feature for MP4 and for HLS, too, if that matters).
Is there a way to get that info from the video?
I post a solution to my problem, In order to get the exact frame time I simply extended MediaCodecVideoTrackRenderer.java class from ExoPlayer library and used the value of lastOutputBufferTimestamp which is in function:
#Override
protected boolean processOutputBuffer(long positionUs, long elapsedRealtimeUs,
MediaCodec codec, ByteBuffer buffer, MediaCodec.BufferInfo bufferInfo, int bufferIndex,
boolean shouldSkip) {
boolean processed = super.processOutputBuffer(positionUs, elapsedRealtimeUs, codec, buffer,
bufferInfo, bufferIndex, shouldSkip);
if (!shouldSkip && processed) {
lastOutputBufferTimestamp = bufferInfo.presentationTimeUs;
}
return processed;
}
It does give me the exact time and not a rounded time from, last say, mPlayer.getDuration() or something like that.
If you have a constant FPS in your video you can calculate that by division and get the number of the frame.
It was simply enough for me to know the exact frame time.
I'm using ExoPlayer version r1.5.3 so I don't know if this solution will work for newer version since code has probably changed.

Jump to a specific time on youtube video (Android)

I already loaded a video on a mobile phone using YouTubePlayerView. I was wondering if anyone could help me with the code to jump to a specific time (e.g., 2m4sec) on a video.
You should use the YouTubePlayer's seekTo methods to jump a video to a specific time.
YouTubePlayer.seekToMillis (int milliSeconds)
More about this method
Or:
YouTubePlayer.seekToRelativeMillis (int milliSeconds)
More about this method
If you want to start a video right away from a certain time, you can use:
YouTubePlayer.loadVideo(String videoId, int timeMillis)
More about this method

VideoView seekto() function extremely inconsistent

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

Categories

Resources