Add PTS to filename without Ctrl/Shift C?

Started by kittmaster, September 11, 2022, 12:34:27 AM

Previous topic - Next topic

kittmaster

I use this program (2.8.1) for outputting frames of video. I have one project that will require thousands of frames from an MP4. Is there a script or a way to add or append the timecode value to the filename without having to keystroke the Ctrl/Shift C and manually doing for each and every frame?

I am looking for a lifeline on this to save days of work if possible.

Thanks for any info.

eumagga0x2a

Quote from: kittmaster on September 11, 2022, 12:34:27 AMI have one project that will require thousands of frames from an MP4.

Does it mean that you save individual frames as images – JPEG or PNG? Do you choose each frame manually or by some simple rule (i.e. all keyframes or frames some fixed time interval apart)?

On Linux (probably on macOS too), you can save the currently displayed picture with its PTS included into filename to a JPEG image by executing

#PY
adm = Avidemux()
ed = Editor()
ui = Gui()

if ed.getRefVideoName(0) is None:
    return

# -------- select output directory --------
outputFolder = ui.dirSelect("Select output folder")
if outputFolder is None:
    ui.displayError("Error", "No output folder selected")
    return

sep = "/"
hmsSep = ":"

ct = ed.getCurrentPts()
ct /= 1000
mss = int(ct)
mss -= int(mss / 1000) * 1000
msecondsString = str(mss)

if mss < 100:
    msecondsString = "0" + msecondsString

if mss < 10:
    msecondsString = "0" + msecondsString

ct /= 1000
ss = int(ct)
ss -= int(ss / 60) * 60
secondsString = str(ss)
if ss < 10:
    secondsString = "0" + secondsString

ct /= 60
mm = int(ct)
mm -= int(mm / 60) * 60
minutesString = str(mm)
if mm < 10:
    minutesString = "0" + minutesString

ct /= 60
hh = int(ct)
hoursString = str(hh)
if hh < 10:
    hoursString = "0" + hoursString

timestamp = hoursString + hmsSep + minutesString + hmsSep + secondsString + "-" + msecondsString

outname = (splitext(ed.getRefVideoName(0)))[0]
outname = basename(outname)
outname += "_" + timestamp + ".jpg"
outname = outputFolder + sep + outname

if not adm.saveJpeg(outname):
    ui.displayError("Error", "Cannot save current frame as \"" + outname + "\"")
    return

ui.displayInfo("Success", "Current frame saved as \"" + outname + "\"")

However, saving this as a *.py file and executing the script via "File" --> "Project Script" --> "Run project" is impractical for use case of saving manually chosed frames as Avidemux always rewinds after running a script file (if you are on Linux or macOS, you can trivially disable this safety precaution by commenting out A_Rewind() at line 256 of avidemux/common/ADM_toolkit/automation.cpp in function call_scriptEngine(const char *scriptFile) and compiling Avidemux from source).

The values of sep and hmsSep are fine for Linux, but you should change them appropriately on Windows.

kittmaster

#2
I was previously finding the needed frame, then save as PNG. Since the frames I am saving are not sequential, the save selection as JPEG is not an option.

I am not doing by keyframe or any specific frame, I am literally using the left right arrow keys, or advancing 10 seconds, or moving the video head (because I know the content) to scrub the frame(s) that is the "clearest" and then actuating the save as PNG. The source material is 480p family video that is being captured for photo albums and the like for family elders.

So when I trigger the save an PNG, I've been manually adding in the timestamp to the filename so I know exactly where the frame comes from. I have one file that is 6 hours in length, so that is why I'm trying to be able to semi automate this particular item.

I have 20TB of storage for this project so if there was a way to export every frame and have it PTS stamped in the PNG filename, I could then pick and choose from the frames and dump the rest...if that is possible?

kittmaster


eumagga0x2a

Quote from: kittmaster on September 12, 2022, 12:27:37 AMif there was a way to export every frame and have it PTS stamped in the PNG filename, I could then pick and choose from the frames and dump the rest...if that is possible?

This is almost trivially possible, just incredibly wasteful. Unfortunately, a much more efficient and easy solution for your problem – to implement adding timestamp to filename internally in C++ – is not something I would welcome in git master as it serves a very specific and rare use case. Such a pity that you are not on Linux where all the necessary infrastructure can be set up by a couple of simple commands.

As you are on Windows, you cannot use timestamps formatted the way Avidemux does directly because on Windows, ":" is illegal in file and directory names, so I replaced them in the script below with HHh-MMm-SSs-XXXms scheme.

adm = Avidemux()
ed = Editor()
ui = Gui()

if not ed.nbSegments():
    ui.displayError("No video", "Please load a video prior to running this script")
    return 0

start = adm.markerA
end = adm.markerB
if start > end:
    swap = end
    end = start
    start = swap

if not adm.setCurrentPts(start):
    ui.displayError("Seek error", "Cannot seek to the start of the selection")
    return 0

outfile = ui.fileWriteSelectEx("Select output filename", "png")
if outfile is None:
    return 0

def getTimeStampString(time):
    ct = int(time / 1000)
    mss = int(ct)
    mss -= int(mss / 1000) * 1000
    msecondsString = str(mss)

    if mss < 100:
        msecondsString = "0" + msecondsString

    if mss < 10:
        msecondsString = "0" + msecondsString

    ct /= 1000
    ss = int(ct)
    ss -= int(ss / 60) * 60
    secondsString = str(ss)
    if ss < 10:
        secondsString = "0" + secondsString

    ct /= 60
    mm = int(ct)
    mm -= int(mm / 60) * 60
    minutesString = str(mm)
    if mm < 10:
        minutesString = "0" + minutesString

    ct /= 60
    hh = int(ct)
    hoursString = str(hh)
    if hh < 10:
        hoursString = "0" + hoursString

    timestamp = hoursString + "h-" + minutesString + "m-" + secondsString + "s-" + msecondsString + "ms"
    return timestamp

count = 0
fileNameNoExtension = (splitext(outfile))[0]

while True:
    pts = ed.getCurrentPts()
    if pts >= end:
        break
    if not adm.savePng(fileNameNoExtension + "-" + getTimeStampString(pts) + ".png"):
        break
    count += 1
    if not adm.seekFrame(1):
        break

if not count:
    ui.displayError("Error", "No PNG images saved")
    return 0

ui.displayInfo("Finished", str(count) + " PNG images saved")
return count

However, running this script to extract all frames of a 6h long 480p video (assuming 30 fps) would not only fill roughly estimated half a terabyte of disk space, it would also take extremely long depending on hardware performance. I recommend to do a test run using a short selection first. On my hardware, this would take ~9 hours.

kittmaster

This worked great, thank you for the support on this. Saved me a boat load of time. All the best, Chris