JuliaIO / VideoIO.jl

Reading and writing of video files in Julia via ffmpeg
https://juliaio.github.io/VideoIO.jl/stable
Other
126 stars 53 forks source link

Inaccuracies with seek #242

Closed yakir12 closed 3 years ago

yakir12 commented 4 years ago

I've had really bizarre behavior from the seek function. In the following MWE :

using VideoIO
function get3rd(f) # get the time stamp of the 3rd frame from the current state
    i = 1
    while !eof(f)
        t = VideoIO.gettime(f)
        read(f) # move one frame forward, this is the only way I know of how to do that btw
        i == 3 && return t
        i += 1
    end
end
file = "a.mp4"
f = VideoIO.openvideo(file)
t = get3rd(f)
for i in 1:5 # some random reads
    read(f)
end
seek(f, t) # ok, let's go to a known time stamp, the one in `t`
t2 = VideoIO.gettime(f) # t2 ≠ t

t2 is not at all equal to t. I've got really weird results in similar situations. Is this behavior expected? If so, I'll try and work around it, but it seems very fickle at times.

yakir12 commented 4 years ago

It's really hard to avoid this issue. Any ideas what's going on? Thanks.

IanButterworth commented 4 years ago

Try seeking to start first so that you seek forwards?

galenlynch commented 3 years ago

I think this is because seek and gettime have different notions of time zero. seek subtracts the first DTS of a stream from the seconds argument, while gettime makes no such adjustment.

yakir12 commented 3 years ago

Interesting... if you have the time (no pun intended) I imagine a PR showing that is the case would be welcomed.

galenlynch commented 3 years ago

@yakir12 This is working on my PR, #279, although with two caveats.

The first caveat is that seeking to a time will position the stream so that the next read(...) call will produce the frame at that time, if a frame exists at that time. This means that for streams where the first frame is at zero seconds (which is the start time of many video streams), you can seek to time zero, and then the next read(...) call will get the first frame.

The second caveat is that with your MWE above, you query the stream time before reading from the stream. gettime tells you the time of the last frame that was read, not the time of the next frame that will be returned.

So if you changed your MWE so that stream time is queried after reading frames, and also read the frame after the seek, then you get your expected results:

using VideoIO # ONLY on PR #279
function get3rd(f) # get the time stamp of the 3rd frame from the current state
    i = 1
    while !eof(f)
        img = read(f) # <- REORDERED
        t = VideoIO.gettime(f) # <- REORDERED
        i == 3 && return (img, t) # <- CHANGED
        i += 1
    end
end
file = "a.mp4"
f = VideoIO.openvideo(file)
img, t = get3rd(f)
for i in 1:5 # some random reads
    read(f)
end
seek(f, t) # ok, let's go to a known time stamp, the one in `t`
t2 = VideoIO.gettime(f) # t2 < t
img3 = read(f) # img == img3
t3 = VideoIO.gettime(f) # t3 == t

Both MWEs are broken on master.