New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FFmpegFrameGrabber.setVideoFrameNumber setting unexpected frames #1697
Comments
Also setTimestamp looks very messy. I would like to write some unit tests for setVideoFrameNumber and setAudioFrameNumber, and extract a clean setTimestamp method for these. Thus the current (big) setTimestamp method can be cleaned up / redone when more specification (unit test) is available By the way at the moment setVideoFrameNumber/setVideoTimestamp uses a fallback to audio timestamp when no video stream is available and vice versa. is this part of the expected behavior? |
/cc @anotherche |
OK, I can see the cause of the problem. I have a suggestion on how to try to fix this, but first I will give my thoughts on the possibilities of using seeking by frame number. The fact is that the seeking by frame number never guarantees hitting the exact frame number, because it always relies on the timestamp, and not on the frame number (there is no information about frame numbers in video streams, we can only calculate their approximate time of presentation). Take this as an axiom. The simplest reason for inaccuracy is a rounding error. But there may also be more severe reasons - variable frame rate, incorrect PTSs, whatever else. So the error can vary from a few microseconds to a few frames. And even one microsecond error may result in "not correct" frame (the previous or the next). By the way, the above code example, when you go sequentially from frame to frame, has very little practical sense (after all, you can just read frames sequentially, right?). But okay, I see a place in the code that is guaranteed to lead to the observed problem in precisely such a meaningless sequential seek method. In lines 758-761 we calculate maximal number of grabs to get to the requested timestamp. I suggest replacing two lines inside the conditional block and check the result:
I believe that in most cases of constant framerate video this should be sufficient for correct determination of boundaries between frames. |
You are right, the code example has no practical use. It is only to demonstrate the difference in output for sequential grabbing and using the setVideoFrameNumber method. The real use case is some kind of video playback in opengl. So we actually jump in time and we do not want to only update an image to the gpu when the frame changes. thus we do our own time to frame calculation |
Yes, I believe it is correct when you know what you want to achieve and use your own algorithm for this task. Because for such a stream seeking, based on the frames indexes, it is almost impossible to create a universal code that suits everyone. For example, I can share a simple technique - if at a jump between two positions in the stream you move forward a small number of frames (within, say, 100), then it is more rational not to use the settimestamp or framenumber, but simply do sequential grabbing until the required place. |
I am fine with the fallback to the other stream in setTimestamp. Like you said you had to choose one of the options. And I failed to see that setTimestamp is not even called by setVideoFrameNumber if no video stream is available.. |
Thanks to @anotherche, this should be fixed with pull #1734. |
The fix has been released with JavaCV 1.5.7, enjoy! Again, thank very much @anotherche! |
version: since 1.5.5 (probably pull #1559 )
setVideoFrameNumber skips to a Frame that is not the expected one
example using a 30fps video:
frameGrabber = new FFmpegFrameGrabber(file); frameGrabber.start(); System.out.println("sequential:"); for (int i = 0; i < 10; i++) { long timestamp = frameGrabber.grabImage().timestamp; System.out.println("number:" + i + " timestamp:" + timestamp); } System.out.println("setVideoFrameNumber:"); for (int i = 0; i < 10; i++) { frameGrabber.setVideoFrameNumber(i); long timestamp = frameGrabber.grabImage().timestamp; System.out.println("number:" + i + " timestamp:" + timestamp); }
creates the output:
sequential: number:0 timestamp:0 number:1 timestamp:33333 number:2 timestamp:66666 number:3 timestamp:100000 number:4 timestamp:133333 number:5 timestamp:166666 number:6 timestamp:200000 number:7 timestamp:233333 number:8 timestamp:266666 number:9 timestamp:300000 setVideoFrameNumber: number:0 timestamp:0 number:1 timestamp:0 number:2 timestamp:66666 number:3 timestamp:100000 number:4 timestamp:100000 number:5 timestamp:166666 number:6 timestamp:200000 number:7 timestamp:200000 number:8 timestamp:266666 number:9 timestamp:300000
I already dug into the setTimestamp method. looks like the fraction of the frameDuration is not handled correctly.
The text was updated successfully, but these errors were encountered: