Getting precise frame number/time corresponding to a frame

Started by butterw, April 23, 2021, 01:09:03 PM

Previous topic - Next topic

butterw

Frame number is used by Avisynth/Virtualdub. From what I understand in current (time-based) Avidemux, the frame number of the current frame is not directly available, also because pts doesn't start at zero in Avidemux, pts time will not typically match the time used in other programs (ffmpeg, Virtualdub, mpv, etc.).
This makes it difficult to convert precisely between an Avidemux project and other tools.

How to get the accurate frame number (and standard time ?) of the current Avidemux frame ?

To get the frame number one approach would be to increment frames (using TinyPy adm.seekFrame(n)) until the timestamp of the current frame is reached. If the whole file is analysed and a index table is saved it would then be possible to get the frame number of any frame much faster.

eumagga0x2a

#1
Quote from: butterw on April 23, 2021, 01:09:03 PMFrom what I understand in current (time-based) Avidemux, the frame number of the current frame is not directly available

This is correct. We can know the picture number only by counting pictures output by decoder, not just by parsing container structures. Please don't forget that frame != picture as frame can be actually a field or be not decodable due to damage.

Quote from: butterw on April 23, 2021, 01:09:03 PMalso because pts doesn't start at zero in Avidemux, pts time will not typically match the time used in other programs (ffmpeg, Virtualdub, mpv, etc.).
This makes it difficult to convert precisely between an Avidemux project and other tools.

In many cases, within a single ref video, it is enough to subtract the PTS of the first picture to translate Avidemux --> FFmpeg. However, the reality is more complex as FFmpeg e.g. doesn't care to restore original exact timestamps when parsing Matroska files, making a roundtrip MP4 --> MKV --> MP4 with standard time bases, lossless in Avidemux, lossy in respect of timing in FFmpeg, if timestamps are not an exact multiple of 1 ms. So, there will be disagreements in many cases, partially due to FFmpeg deficiencies.

Quote from: butterw on April 23, 2021, 01:09:03 PMTo get the frame number one approach would be to increment frames (using TinyPy adm.seekFrame(n)) until the timestamp of the current frame is reached. If the whole file is analysed and a index table is saved it would then be possible to get the frame number of any frame much faster.

If you stick with tinyPy and as long as no filtering is involved, yes, this will do exactly that. If you would like to do that in C++, a muxer (actually, a video filter or dummy video encoder could do it as well) plugin would be the way to go.

butterw

uses 2 frame-step values (coarse then fine) to speed-up the seeking.

fstep = 150 #initial frame-step

adm=Avidemux(); ed=Editor(); gui=Gui()
if ed.nbSegments() <1: print("! nbSegments must be 1+"); return

c_pts = ed.getCurrentPts()
frm=0
adm.setCurrentPts(0) 
pts = ed.getCurrentPts()
while pts <c_pts:
prev_pts = ed.getCurrentPts()
adm.seekFrame(fstep)
frm+= fstep
pts = ed.getCurrentPts()
if frm > fstep:
adm.setCurrentPts(prev_pts)
frm-= fstep
else:
adm.setCurrentPts(0); frm=0
while ed.getCurrentPts()<c_pts:
adm.seekFrame(2)
frm+= 2
if ed.getCurrentPts()>c_pts: frm-= 1
gui.displayError("current frame:", str(frm))

xyzzy

I found that I could get the offset of the avidemux timestamp from the PTS by looking at the idx2 file that avidemux makes.  The first pts from the audio or video stream, whichever is first, will be the time at zero.

It would great if avidemux could save this number into a project file.  It would make it much easier to do something outside of avidemux on the source media of an edit.  E.g., extract subtitles or truncate a TS capture to the end of a program.

butterw

Updated code 
default starting point is frm, pts_start = (0, 0)

adm=Avidemux(); ed=Editor(); gui=Gui(); sec=1000*1000
if ed.nbSegments() <1: print("! nbSegments must be 1+"); return

c_pts = ed.getCurrentPts()
##Starting point (frm, pts0):
frm, pts_start = (0, 0) ##default start
# frm, pts_start = (900, 30066666)
if (pts_start>c_pts): gui.displayError("! starting point: pts_start>c_pts", str(pts_start)); return
adm.setCurrentPts(pts_start)
pts = ed.getCurrentPts()
pts0 = 0; if pts_start==0: pts0 = pts #first frame time offset
done = (pts==c_pts)
cond = (pts<c_pts)
if not cond: return
for frm_step in [150, 30, 2, 1]:
while cond:
prev_pts = pts
seek_ok = adm.seekFrame(frm_step)
pts = ed.getCurrentPts()
cond= seek_ok and pts<c_pts
done= done or (seek_ok and pts==c_pts)
if cond or done: frm+= frm_step
if not done:
adm.setCurrentPts(prev_pts)
pts = ed.getCurrentPts()
# gui.displayError(str(frm_step)+" frm_step:", str(frm));
cond = True
done_str="!!!"; if done: done_str= "Done,"
gui.displayError(done_str +" current frame:", str(frm) +"\n"+ str((pts-pts0)*1/sec)+"s")

butterw

Quote from: xyzzy on April 25, 2021, 09:48:36 AMI found that I could get the offset of the avidemux timestamp from the PTS by looking at the idx2 file that avidemux makes.  The first pts from the audio or video stream, whichever is first, will be the time at zero.

It would great if avidemux could save this number into a project file.  It would make it much easier to do something outside of avidemux on the source media of an edit.  E.g., extract subtitles or truncate a TS capture to the end of a program.

You can set markerA to the first frame before saving.

eumagga0x2a

The hardest, but IMHO probably the most rewarding path would be a semi-rewrite of the application to use the signed 64-bit integer (int64_t instead of uint64_t) data type for timestamps.

xyzzy

Quote from: butterw on April 25, 2021, 11:51:18 AMYou can set markerA to the first frame before saving.

But doesn't this give avidemux timestamp of 1st frame?  I want to know the PTS value from the source media file of avidemux timestamp 0.

butterw

Quote from: xyzzy on April 26, 2021, 06:58:44 AM
Quote from: butterw on April 25, 2021, 11:51:18 AMYou can set markerA to the first frame before saving.

But doesn't this give avidemux timestamp of 1st frame?
Yes, uint64_t Avidemux timestamp as used in Avidemux project files.
Most other tools set this offset to zero, so saving this value could be useful (though maybe it isn't the value you were looking for).
Processing a .py project with an external python script is easy, but the relevant information needs to be included in the project file.

instead of using the selection marker A for this: adm.markerA = 66666
it would be better to had some (ex: 10) non-selection markers to Avidemux, saved to the project file (when they are set):
adm.marker0 = 66666