Log in

View Full Version : A stutter-correction script--howto?


Dark Shikari
7th July 2007, 20:36
I've been thinking of making a script for correcting stutter in FRAPS videos I make; that is, sometimes in the video FRAPS will drop a few frames, and I figure MVTools could be used to fill in those frames. It could also be used for FRAPS videos that are recorded at 30 FPS but end up only recording at 15 FPS (so frames are randomly duped to fit by FRAPS).

However, such frames will be throughout the FRAPS videos, and so it will be somewhat difficult to manually pinpoint each one. Fortunately, since the FRAPS files are very high quality, it should be very easy to find duplicate frames.

What would be a good way to find such duplicate frames (might be 2, 3, 4 in a row, who knows) and automatically replace them with frames created by MVTools?

Dark Shikari
10th July 2007, 05:35
Any ideas?

gzarkadas
10th July 2007, 23:36
Assuming you can construct an mvtools-compensated clip from your source without need to know a-priori which frames are duplicate, you can try the following script (the code to create the motion-compensated clip is assumed to be in a function so that last is not overriden, because it holds the source clip that will be passed to ScriptClip):

global threshold = 0.05 # adjust so that only dups are detected
AviSource(...) # your input clip
global mv_clp = your_mvtools_compensated_clip(last)
ScriptClip("""(YDifferenceFromPrevious() <= threshold && \
UDifferenceFromPrevious() <= threshold && \
VDifferenceFromPrevious() <= threshold) ? mv_clp : last""")

The metric used to detect duplicates may need tweaking if your duplicates are not exactly the same.

Dark Shikari
11th July 2007, 06:30
Assuming you can construct an mvtools-compensated clip from your source without need to know a-priori which frames are duplicate, you can try the following script (the code to create the motion-compensated clip is assumed to be in a function so that last is not overriden, because it holds the source clip that will be passed to ScriptClip):

global threshold = 0.05 # adjust so that only dups are detected
AviSource(...) # your input clip
global mv_clp = your_mvtools_compensated_clip(last)
ScriptClip("""(YDifferenceFromPrevious() <= threshold && \
UDifferenceFromPrevious() <= threshold && \
VDifferenceFromPrevious() <= threshold) ? mv_clp : last""")

The metric used to detect duplicates may need tweaking if your duplicates are not exactly the same.
What do you mean by a motion-compensated clip?

The original already has every frame it needs for 30 FPS, so the only way to insert motion-compensated frames is to remove the ones that already exist.

Dark Shikari
11th July 2007, 06:57
Here's some pseudocode for my idea (done in C-style); I don't know if its possible to do it entirely in Avisynth:

clip source = DirectShowSource("file.avi")
clip finalClip
for(int frame = 0; frame < file.length(); frame++)
{
If(isDuplicate(n,n+1))
{
int numDuplicates = 0;
while(isDuplicate(n,n+1));
{
n++;
numDuplicates++;
}
workingClip = Trim(n-numDuplicates,n-numDuplicates) + Trim(n,n);
workingClip = workingClip.AssumeFPS(2);
//Do MVAnalyze stuff here
workingClip=workingClip.MVFlowFPS2(num=numDuplicates+2); //Change the FPS from 2 (the two frames) to 2+numDuplicates
finalClip = finalClip + workingClip
}
else finalClip = finalClip + Trim(n,n);
}

gzarkadas
11th July 2007, 15:42
What do you mean by a motion-compensated clip?

The one you will construct to make motion-compensation (the //Do MVAnalyze stuff here... in your next post)

The original already has every frame it needs for 30 FPS, so the only way to insert motion-compensated frames is to remove the ones that already exist.

That's what the conditional statement in ScripClip's script does.

If you need to know the number of duplicates beforehand to make the motion interpolation, then a two-pass algorithm would be more appropriate:

Pass 1: discover the duplicates; store info in a text file
Pass 2: read text file with ConditionalReader and replace duplicates with motion-compensated frames

However, summing the number of dups is a bit tricky; I will give it a try, but you will have to be patient :).

Dark Shikari
11th July 2007, 19:12
The one you will construct to make motion-compensation (the //Do MVAnalyze stuff here... in your next post)



That's what the conditional statement in ScripClip's script does.

If you need to know the number of duplicates beforehand to make the motion interpolation, then a two-pass algorithm would be more appropriate:

Pass 1: discover the duplicates; store info in a text file
Pass 2: read text file with ConditionalReader and replace duplicates with motion-compensated frames

However, summing the number of dups is a bit tricky; I will give it a try, but you will have to be patient :).
Thanks, that makes sense.

Oh, just to note, the source is FRAPS, so there will be little to no noise, making duplicate detection very easy (low threshold).

I'll fool around with the script earlier and see how effective it is. Twopass is fine I think--just two scripts, one run after another. I don't think I have enough experience with AviSynth to get it all working myself though so your try will be appreciated.

gzarkadas
11th July 2007, 22:48
Ok, here is what I came up. I have verified that the dup-detection logic is correct; you will only have to supply your motion compensation function in the pass-2 script.

Wrap all motion compensation code in a function that you declare outside the runtime script. Also, if possible, move everything that does not depend in fs, fe in the main script) to optimise speed.

Pass 1 (only creates the __dups__.txt text file; just play the script - no need to encode)

global threshold = 0.001 # adjust so that only dups are detected
global SPC = " "
AviSource(...) # your input clip
global dup_count = 0
global last_dup_count = 0
header = """type int
default 0
"""
WriteFileStart("__dups__.txt", "header")
ScriptClip("""
isdup = current_frame > 0 && \
YDifferenceFromPrevious() <= threshold && \
UDifferenceFromPrevious() <= threshold && \
VDifferenceFromPrevious() <= threshold
global last_dup_count = dup_count
global dup_count = isdup ? dup_count + 1 : 0
can_output = isdup ? false : true
must_output = can_output && last_dup_count > 0
fdup_fst = must_output ? current_frame - last_dup_count - 1 : -1
fdup_cnt = last_dup_count
global last_dup_count = must_output ? 0 : last_dup_count
return must_output \
? WriteFile(last, "__dups__.txt", "fdup_fst", "SPC", "fdup_cnt") \
: last
""")


Pass 2 (replaces the dups - with the help of your code)

# your custom function
function make_mvclip(clip clp, int fr_start, fr_end) {
# fr_start is the first (original) frame of the dup series
# fr_end is the first frame after the dup series
...
# your code in here
}

global dup_count = 0 # input var
global dup_start = 0
global cur_dups = 0
AviSource(...) # your input clip
# do MVAnalyze stuff here ??
ScriptClip("""
global dup_start = dup_count > 0 ? current_frame : dup_start
global cur_dups = dup_count > 0 ? dup_count : cur_dups
fs = dup_start # first frame of dup series
fe = dup_start + cur_dups + 1 # first frame after dups
in_dups = dup_start > 0 && current_frame >= fs && current_frame <= fe

# do MVAnalyze stuff here ?? (if yes, as little as possible)
# make an (fe - fs) frames clip; you must supply the function
mv_clp = in_dups ? make_mvclip(last, fs, fe) : NOP

global dup_start = in_dups ? dup_start : 0
global cur_dups = in_dups ? cur_dups : 0
return in_dups ? mv_clp.Trim(current_frame - fs, -1) : last
""")
ConditionalReader("__dups__.txt", "dup_count", true)


PS: I assumed your clip is YUV; if not you will have to use RGBDifferenceFromPrevious in the isdup... test of the pass-1 script.

Dark Shikari
12th July 2007, 05:13
Ok, here is what I came up. I have verified that the dup-detection logic is correct; you will only have to supply your motion compensation function in the pass-2 script.

Wrap all motion compensation code in a function that you declare outside the runtime script. Also, if possible, move everything that does not depend in fs, fe in the main script) to optimise speed.

Pass 1 (only creates the __dups__.txt text file; just play the script - no need to encode)

global threshold = 0.001 # adjust so that only dups are detected
global SPC = " "
AviSource(...) # your input clip
global dup_count = 0
global last_dup_count = 0
header = """type int
default 0
"""
WriteFileStart("__dups__.txt", "header")
ScriptClip("""
isdup = current_frame > 0 && \
YDifferenceFromPrevious() <= threshold && \
UDifferenceFromPrevious() <= threshold && \
VDifferenceFromPrevious() <= threshold
global last_dup_count = dup_count
global dup_count = isdup ? dup_count + 1 : 0
can_output = isdup ? false : true
must_output = can_output && last_dup_count > 0
fdup_fst = must_output ? current_frame - last_dup_count - 1 : -1
fdup_cnt = last_dup_count
global last_dup_count = must_output ? 0 : last_dup_count
return must_output \
? WriteFile(last, "__dups__.txt", "fdup_fst", "SPC", "fdup_cnt") \
: last
""")


Pass 2 (replaces the dups - with the help of your code)

# your custom function
function make_mvclip(clip clp, int fr_start, fr_end) {
# fr_start is the first (original) frame of the dup series
# fr_end is the first frame after the dup series
...
# your code in here
}

global dup_count = 0 # input var
global dup_start = 0
global cur_dups = 0
AviSource(...) # your input clip
# do MVAnalyze stuff here ??
ScriptClip("""
global dup_start = dup_count > 0 ? current_frame : dup_start
global cur_dups = dup_count > 0 ? dup_count : cur_dups
fs = dup_start # first frame of dup series
fe = dup_start + cur_dups + 1 # first frame after dups
in_dups = dup_start > 0 && current_frame >= fs && current_frame <= fe

# do MVAnalyze stuff here ?? (if yes, as little as possible)
# make an (fe - fs) frames clip; you must supply the function
mv_clp = in_dups ? make_mvclip(last, fs, fe) : NOP

global dup_start = in_dups ? dup_start : 0
global cur_dups = in_dups ? cur_dups : 0
return in_dups ? mv_clp.Trim(current_frame - fs, -1) : last
""")
ConditionalReader("__dups__.txt", "dup_count", true)


PS: I assumed your clip is YUV; if not you will have to use RGBDifferenceFromPrevious in the isdup... test of the pass-1 script.
The clips are RGB I think but I'll use YUV anyways as all the footage is going to be converted to YV12 or YUV for encoding anyways.

Thanks a lot for the script: I'll experiment with it and see how well it works.

Dark Shikari
12th July 2007, 07:04
OK, after a good bit of futzing I got it to work... for the first few frames.

Then, it says "System Exception - Access Violation" during the MVTools functions :confused:

Dark Shikari
12th July 2007, 08:15
Exception fixed, but now the script doesn't seem to work: the MVTools function only runs once, so every single set of interpolated frames looks like the first set of interpolated frames :confused:

Dark Shikari
12th July 2007, 09:11
AND IT WORKS!

The problem was the IDX parameter--it was saving the results between clips!

Doing this:

backward_vec = shortclip.MVAnalyse(blksize=8,isb = true, truemotion=true, pel=2, idx=fr_start)
forward_vec = shortclip.MVAnalyse(blksize=8,isb = false, truemotion=true, pel=2, idx=fr_start)
cropped = shortclip.crop(4,4,-4,-4) # by half of block size 8
backward_vec2 = shortclip.MVAnalyse(blksize=8,isb = true, truemotion=true, pel=2, idx=fr_start+1)
forward_vec2 = shortclip.MVAnalyse(blksize=8,isb = false, truemotion=true, pel=2, idx=fr_start+1)

fixed it! Awesome :cool:

Dark Shikari
12th July 2007, 09:12
Final 2nd-pass script:

function make_mvclip(clip clp, int fr_start, fr_end) {
shortclip=clp.Trim(fr_start,fr_start) + clp.Trim(fr_end, fr_end)
shortclip=shortclip.AssumeFPS(2)
backward_vec = shortclip.MVAnalyse(blksize=8,isb = true, truemotion=true, pel=2, idx=fr_start, dct=4, search=2, searchparam=4)
forward_vec = shortclip.MVAnalyse(blksize=8,isb = false, truemotion=true, pel=2, idx=fr_start, dct=4, search=2, searchparam=4)
cropped = shortclip.crop(4,4,-4,-4) # by half of block size 8
backward_vec2 = shortclip.MVAnalyse(blksize=8,isb = true, truemotion=true, pel=2, idx=fr_start+1, dct=4, search=2, searchparam=4)
forward_vec2 = shortclip.MVAnalyse(blksize=8,isb = false, truemotion=true, pel=2, idx=fr_start+1, dct=4, search=2, searchparam=4)
shortclip.MVFlowFps2(backward_vec,forward_vec,backward_vec2,forward_vec2,num=fr_end-fr_start+2,idx=fr_start,idx2=fr_start+1)
}
global dup_count = 0
global dup_start = 0
global cur_dups = 0
AVISource("video.avi").ConvertToYV12(matrix="rec709") # your input clip
ScriptClip("""
global dup_start = dup_count > 0 ? current_frame : dup_start
global cur_dups = dup_count > 0 ? dup_count : cur_dups
fs = dup_start # first frame of dup series
fe = dup_start + cur_dups + 1 # first frame after dups
in_dups = dup_start > 0 && current_frame >= fs && current_frame <= fe
mv_clp = in_dups ? make_mvclip(last, fs, fe) : NOP
global dup_start = in_dups ? dup_start : 0
global cur_dups = in_dups ? cur_dups : 0
return in_dups ? mv_clp.Trim(current_frame - fs, -1) : last
""")
ConditionalReader("__dups__.txt", "dup_count")


Note there appear to be some Vdub crashing issues now, so don't cheer just yet...

Dark Shikari
12th July 2007, 09:23
Ugh...

If I use IDX, the memory blows up pretty fast but it crashes with an access violation long before it hits AviSynth's max.

If I don't use IDX, it crashes after like 2-3 frames :rolleyes:

Author of MVTools needs to check his array bounds or something :mad: according to VirtualDub the plugin appears to be trashing the stack.

In the meantime I'll see if I can fix his code myself...

Didée
12th July 2007, 10:23
Though I'm not the author, I had seen this problem rising ...
MVTools are very complex internally, and using it inside the conditional environment made me frown ...

Quick try: what happens if you remove all idx parameters, and specify "pel=1" in MVAnalyze? (Quality will drop, but that can be fixed. Important is if things get stable this way.)

edit: Uh, it's MVFlowFPS2() ... that one most probably won't work without idx. Sounds like MVFlowFPS() with overlapped blocks had to be used then (if the script basically does work, without idx).

Dark Shikari
12th July 2007, 11:11
Though I'm not the author, I had seen this problem rising ...
MVTools are very complex internally, and using it inside the conditional environment made me frown ...

Quick try: what happens if you remove all idx parameters, and specify "pel=1" in MVAnalyze? (Quality will drop, but that can be fixed. Important is if things get stable this way.)

edit: Uh, it's MVFlowFPS2() ... that one most probably won't work without idx. Sounds like MVFlowFPS() with overlapped blocks had to be used then (if the script basically does work, without idx).
I'll try that.

Another thought--is there a program that can read an AviSynth script by reading X frames, closing it, reading X frames, closing it, etc? This would clean up all the memory used by MVTools and avoid any crashes.

Dark Shikari
12th July 2007, 11:14
Didn't work. What was used:

backward_vec = shortclip.MVAnalyse(blksize=8,isb = true, truemotion=true, pel=1)
forward_vec = shortclip.MVAnalyse(blksize=8,isb = false, truemotion=true, pel=1)
shortclip.MVFlowFps(backward_vec,forward_vec,num=fr_end-fr_start+2)

What happened after 10 frames:

An out-of-bounds memory access (access violation) occurred in module 'VirtualDub'...
...reading address 00000009.

Halfpel precision isn't that necessary in this case as almost all the input video is at very high resolutions to begin with (HD), but it doesn't even work without it...

I'm thinking my idea from earlier (read X frames, close AVS, open AVS, read X frames) would work if there was an easy way to do that.

Didée
12th July 2007, 11:43
Well, you might try s-th like

ScriptClip( """ AviSource( SomeAvsScript.avs ) """ )

But that way it gets difficult to make necessary variables/values available to the inner avs script...

I'd just take it that MVTools are not made for such dynamic-arbitrary work.

BTW, what's the average maximum count of duplicates you're getting? Things would be way easier if one could pre-prepare a given set of interpolations (say, a 4-set for drops=1 / drops=2 / drops=3 / drop=4), and choose the corresponding one for the actual spot.
Of course, if you want to repair up to like 20 dropped frames in a row, then pre-prepairing is not a good strategy either.

Dark Shikari
12th July 2007, 11:53
Well, you might try s-th like

ScriptClip( """ AviSource( SomeAvsScript.avs ) """ )

Oh god.

That actually might work, but its hilariously wrong...

BTW, what's the average maximum count of duplicates you're getting? Things would be way easier if one could pre-prepare a given set of interpolations (say, a 4-set for drops=1 / drops=2 / drops=3 / drop=4), and choose the corresponding one for the actual spot.
Of course, if you want to repair up to like 20 dropped frames in a row, then pre-prepairing is not a good strategy either.
Mostly 2-5 or so but there could be stutters in which up to 20-30 frames are lost (i.e. a one-second stutter).

I'll try scripting the script inside the script. :eek:

Didée
12th July 2007, 12:09
That actually might work, but its hilariously wrong...

"sore bungling" (if that's a possible english expression) , sure. :D

Dark Shikari
12th July 2007, 12:29
"sore bungling" (if that's a possible english expression) , sure. :D
Would I have to modify the script at all to get it to work? I'm getting all kinds of weird artifacts with it, like black bars and broken chroma planes.

gzarkadas
12th July 2007, 15:17
The frame-rate setting in MVFlowFPS2 was off by one frame. Also I made an optimisation to the runtime script so that the mv-clip is created once only for each dup series.

I believe it should work now :) (I have run it succesfully with one ~400 frames test clip having three groups of 3, 12 and 7 dups without any errors).


function make_mvclip(clip clp, int fr_start, int fr_end) {
shortclip = clp.Trim(fr_start, -1) + clp.Trim(fr_end, -1)
shortclip = shortclip.AssumeFPS(2)
backward_vec = shortclip.MVAnalyse(blksize=8, isb=true, truemotion=true, pel=2, \
idx=fr_start, dct=4, search=2, searchparam=4)
forward_vec = shortclip.MVAnalyse(blksize=8, isb=false, truemotion=true, pel=2, \
idx=fr_start, dct=4, search=2, searchparam=4)
cropped = shortclip.Crop(4, 4, -4, -4) # by half of block size 8
backward_vec2 = shortclip.MVAnalyse(blksize=8, isb=true, truemotion=true, pel=2, \
idx=fr_start+1, dct=4, search=2, searchparam=4)
forward_vec2 = shortclip.MVAnalyse(blksize=8, isb=false, truemotion=true, pel=2, \
idx=fr_start+1, dct=4, search=2, searchparam=4)
return shortclip.MVFlowFps2(backward_vec, forward_vec, backward_vec2, forward_vec2, \
num=fr_end-fr_start+1, idx=fr_start, idx2=fr_start+1)
}

global dup_count = 0
global dup_start = 0
global cur_dups = 0
AVISource("video.avi").ConvertToYV12(matrix="rec709") # your input clip
global mv_clp = 0
global source_clp = last
ScriptClip("""
global dup_start = dup_count > 0 ? current_frame : dup_start
global cur_dups = dup_count > 0 ? dup_count : cur_dups
fs = dup_start # first frame of dup series
fe = dup_start + cur_dups + 1 # first frame after dups
in_dups = dup_start > 0 && current_frame >= fs && current_frame <= fe
global mv_clp = dup_count > 0 ? make_mvclip(source_clp, fs, fe) : mv_clp
global dup_start = in_dups ? dup_start : 0
global cur_dups = in_dups ? cur_dups : 0
return in_dups ? mv_clp.Trim(current_frame - fs, -1) : last
""")
ConditionalReader("__dups__.txt", "dup_count")

Dark Shikari
12th July 2007, 17:52
After two dozen frames or so, during playback in Media Player Classic [it fails silently in Virtualdub, crashing the program without message]:

The instruction at "0x022a6a6f" referenced memory at "0x7c7d7e61". The memory could not be "read".

My clip is one where each frame is separated by 1-5 duplicates.

(It doesn't crash as fast as before though!)

gzarkadas
12th July 2007, 22:26
Yes, it is a not-enough-memory "error"; the script hits quickly the 2GB process size limit on Win32 for large clips.

I have made a test script with a 9250 frames clip (i used this code fragments below to generate the clip)

function make_dups(clip clp, int fs, int cnt) {
Assert(fs >= 0 && fs + cnt < clp.Framecount, "make_dups: Invalid fs")
return fs == 0 ? \
clp.Trim(fs, -1).Loop(cnt) + clp.Trim(fs+cnt, 0) : \
clp.Trim(0, -fs) + clp.Trim(fs, -1).Loop(cnt) + clp.Trim(fs+cnt, 0)
}
...
AviSource("clip-to-detect.avi") # a 370 frames clip

last = last.make_dups(0, 2).make_dups(2, 15).make_dups(17, 3).make_dups(20, 3 \
).make_dups(23, 2).make_dups(25, 4).make_dups(29, 8).make_dups(37, 2).make_dups(39, 6 \
).make_dups(45, 4).make_dups(49, 5).make_dups(54, 25).make_dups(79, 44).make_dups(123, 4 \
).make_dups(145, 87).make_dups(232, 104).make_dups(336, 2).make_dups(338, 12 \
).make_dups(350, 17)
last = last + last + last + last + last # 1850 frames
last = last + last + last + last + last # 9250 frames


After about ~1500 frames the script shows regularly inside frames the message:

Evaluate: Unrecognised exception!
({5 garbage chars}, line 14)
([ScriptClip ], line 7)

The offending line in the runtime script is the one that calls make_mvclip. Vdub process size is ~1970100KB (as reported by Process Explorer). The script manages to finish though (input playback; didn;t encoded). Probably mvtools fails to allocate memory for its structures due to memory shortage and thus the error message above appears.

Versions:
Avisynth 2.5.7
VirtualDub 1.6.11
MVTools 1.6.2

Unfortunately, you will have to proceed by dividing the clip into smaller chunks and process them one by one, then join. The text file produced by the pass-1 script can provide the info for where to put the cut points.

Dark Shikari
13th July 2007, 03:44
Yes, it is a not-enough-memory "error"; the script hits quickly the 2GB process size limit on Win32 for large clips.

I have made a test script with a 9250 frames clip (i used this code fragments below to generate the clip)

function make_dups(clip clp, int fs, int cnt) {
Assert(fs >= 0 && fs + cnt < clp.Framecount, "make_dups: Invalid fs")
return fs == 0 ? \
clp.Trim(fs, -1).Loop(cnt) + clp.Trim(fs+cnt, 0) : \
clp.Trim(0, -fs) + clp.Trim(fs, -1).Loop(cnt) + clp.Trim(fs+cnt, 0)
}
...
AviSource("clip-to-detect.avi") # a 370 frames clip

last = last.make_dups(0, 2).make_dups(2, 15).make_dups(17, 3).make_dups(20, 3 \
).make_dups(23, 2).make_dups(25, 4).make_dups(29, 8).make_dups(37, 2).make_dups(39, 6 \
).make_dups(45, 4).make_dups(49, 5).make_dups(54, 25).make_dups(79, 44).make_dups(123, 4 \
).make_dups(145, 87).make_dups(232, 104).make_dups(336, 2).make_dups(338, 12 \
).make_dups(350, 17)
last = last + last + last + last + last # 1850 frames
last = last + last + last + last + last # 9250 frames


After about ~1500 frames the script shows regularly inside frames the message:

Evaluate: Unrecognised exception!
({5 garbage chars}, line 14)
([ScriptClip ], line 7)

The offending line in the runtime script is the one that calls make_mvclip. Vdub process size is ~1970100KB (as reported by Process Explorer). The script manages to finish though (input playback; didn;t encoded). Probably mvtools fails to allocate memory for its structures due to memory shortage and thus the error message above appears.

Versions:
Avisynth 2.5.7
VirtualDub 1.6.11
MVTools 1.6.2

Unfortunately, you will have to proceed by dividing the clip into smaller chunks and process them one by one, then join. The text file produced by the pass-1 script can provide the info for where to put the cut points.
Ugh, I was hoping to make this more automated than that! For clips of tens of thousands of frames this will be completely impractical without automation.

foxyshadis
13th July 2007, 11:17
You'd definitely need a custom mvtools build if you're going to use it in scriptclip like that. Fear not, intrepid readers, for there is hope!

Call the mvanalyse functions just once! Pick two idx values, 1 and 2 work. Make their infoclips global. Then you can replace all of make_mvclip with:
function make_mvclip(clip clp, int fr_start, int fr_end) {
shortclip = clp.Trim(fr_start, -1) + clp.Trim(fr_end, -1)
shortclip = shortclip.AssumeFPS(2)

return shortclip.MVFlowFps2(backward_vec, forward_vec, backward_vec2, forward_vec2, \
num=fr_end-fr_start+1, idx=1, idx2=2)
}
You'll still get a steady memory climb, but it should be significantly smaller. Too tired to think of better fixes now.

Also, Shikari, you might email Fraps, as I did a while back, and ask them if they could provide a tweakable cpu/size algorithm. 99% of the time I get dropped frames because it's running the disk at full speed and the game accesses something, meanwhile I have like 80% cpu free. It's maddening.

Dark Shikari
14th July 2007, 22:09
I solved it anyways over the past two days (had no internet though) with the most ugly hack I have ever created: but it WORKS! Its a single-click script!

I'll upload the full version after I do some more testing, but just to give you an idea of the ugliness:

Minimal Cygwin environment
runs a shell script that
runs a loop that
assembles an AviSynth script that
calls the main AviSynth script, trimmed to a small group of frames that
calls the motion estimation function that
creates the intermediate frames
assembles a VirtualDub job script that
is used by VirtualDub to assemble a single AVI file
assembles a part of an AviSynth script that assembles all of the AVI files created by the loop
runs the aforementioned AviSynth script to assemble all of the AVI files

But it works :D and it works well too :cool:

Dark Shikari
14th July 2007, 22:14
Heck, I'll post the bash script here for now, I'll upload the final package with all important files tomorrow.

function strcat ()
{
local s1_val s2_val;
s1_val=${!1};
s2_val=${!2};
eval "$1"=\'"${s1_val}${s2_val}"\'
}
./echo
./echo "StutterFixer: uses motion estimation, interpolation, and compensation"
./echo "to fix stutters in videos."
./echo "Copyright (C) 2007 Dark Shikari"
./echo "This program is free software; you can redistribute it and/or modify"
./echo "it under the terms of the GNU General Public License as published by"
./echo "the Free Software Foundation; either version 2 of the License, or"
./echo "(at your option) any later version."
./echo
./echo "This program is distributed in the hope that it will be useful,"
./echo "but WITHOUT ANY WARRANTY; without even the implied warranty of"
./echo "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the"
./echo "GNU General Public License for more details."
./echo
./echo "You should have received a copy of the GNU General Public License"
./echo "along with this program; if not, write to the Free Software"
./echo "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"
./echo
./echo "Starting motion-compensated stutter/low-framerate fixer."
./echo
./echo "It is highly recommended that you have about eight times"
./echo "the size of your input FRAPS file in free space for"
./echo "temporary files."
./echo
./echo "Running first pass."
./echo
h='VirtualDub.Open("stutterFixerPass1.avs");VirtualDub.audio.SetSource(0);VirtualDub.audio.SetMode(0);VirtualDub.audio.SetInterleave(1,500,1,0,0);VirtualDub.audio.SetClipMode(1,1);VirtualDub.audio.SetConversion(0,0,0,0,0);VirtualDub.audio.SetVolume();VirtualDub.audio.SetCompression();VirtualDub.audio.EnableFilterGraph(0);VirtualDub.video.SetInputFormat(0);VirtualDub.video.SetOutputFormat(7);VirtualDub.video.SetMode(1);VirtualDub.video.SetSmartRendering(0);VirtualDub.video.SetPreserveEmptyFrames(0);VirtualDub.video.SetFrameRate(0,1);VirtualDub.video.SetIVTC(0,0,-1,0);VirtualDub.video.SetCompression(0x75796668,0,10000,0);VirtualDub.video.filters.Clear();VirtualDub.audio.filters.Clear();VirtualDub.SaveAVI("temp.avi");VirtualDub.Close();'
./echo $h > script.vcf
./rm -f temp.avi
./echo "Compiling duplicate list [first pass]."
./vdub.exe /s "script.vcf"
./rm -f temp.avi
./echo
./echo "Running second pass: this may take a while!"
./echo
fileList=""
plus=""
currentI=0
previousI=0
counter=0
addToCounter="+1"
./echo "SetMemoryMax(128)" > readFiles.avs
./rm -rf output
./mkdir output
for i in $(./sed -n 4~1p __dups__.txt | ./sed -e s/\ \ \[0-9]//g | ./sed -e s/\ \[0-9]//g | ./sed -e s/\ \[0-9]//g | ./sed -e s/\ \[0-9]//g)
do a='AVISource("stutterFixerPass2.avs").Trim('
strcat fileList plus
previousI=$currentI
currentI=$i
b=$i
c="-"
d=$previousI
strcat a d
e=","
strcat a e
b=$i
c="-1"
strcat b c
d=$(./echo "$b" | ./bc)
strcat a d
f=")"
strcat a f
./echo $a > callStutterFix.avs
g='VirtualDub.Open("callStutterFix.avs");'
./echo $g > script.vcf
h="VirtualDub.audio.SetSource(0);VirtualDub.audio.SetMode(0);VirtualDub.audio.SetInterleave(1,500,1,0,0);VirtualDub.audio.SetClipMode(1,1);VirtualDub.audio.SetConversion(0,0,0,0,0);VirtualDub.audio.SetVolume();VirtualDub.audio.SetCompression();VirtualDub.audio.EnableFilterGraph(0);VirtualDub.video.SetInputFormat(0);VirtualDub.video.SetOutputFormat(15);VirtualDub.video.SetMode(2);VirtualDub.video.SetSmartRendering(0);VirtualDub.video.SetPreserveEmptyFrames(0);VirtualDub.video.SetFrameRate(33333,1);VirtualDub.video.SetIVTC(0,0,-1,0);VirtualDub.video.SetCompression();VirtualDub.video.filters.Clear();VirtualDub.audio.filters.Clear();"
./echo $h >> script.vcf
j=$i
k=$counter
strcat k addToCounter
counter=$(./echo "$k" | ./bc)
l=$counter
m='VirtualDub.SaveAVI("output/'
n='.avi");'
z=$l
strcat l n
strcat m l
./echo $m >> script.vcf
o="VirtualDub.Close();"
./echo $o >> script.vcf
./echo "Running VirtualDub..."
./vdub.exe /s "script.vcf"
p=".avi"
y=$z
strcat y p
q='=AVISource("output/'
strcat q y
r='")'
strcat q r
s="a"
strcat s z
t=$s
strcat t q
./echo $t >> readFiles.avs
strcat fileList s
plus="+"
done
./echo $fileList >> readFiles.avs
h='VirtualDub.Open("readFiles.avs");VirtualDub.audio.SetSource(0);VirtualDub.audio.SetMode(0);VirtualDub.audio.SetInterleave(1,500,1,0,0);VirtualDub.audio.SetClipMode(1,1);VirtualDub.audio.SetConversion(0,0,0,0,0);VirtualDub.audio.SetVolume();VirtualDub.audio.SetCompression();VirtualDub.audio.EnableFilterGraph(0);VirtualDub.video.SetInputFormat(0);VirtualDub.video.SetOutputFormat(7);VirtualDub.video.SetMode(1);VirtualDub.video.SetSmartRendering(0);VirtualDub.video.SetPreserveEmptyFrames(0);VirtualDub.video.SetFrameRate(0,1);VirtualDub.video.SetIVTC(0,0,-1,0);VirtualDub.video.SetCompression(0x75796668,0,10000,0);VirtualDub.video.filters.Clear();VirtualDub.audio.filters.Clear();VirtualDub.SaveAVI("result.avi");VirtualDub.Close();'
./echo $h > script.vcf
./rm -f result.avi
./echo "Compiling final video..."
./vdub.exe /s "script.vcf"
./echo "Deleting temporary files..."
./rm -rf output
./rm -f script.vcf
./rm -f callStutterFix.avs
./rm -f readFiles.avs
Yes. It does what you think it does. Yes, its that bad.

Dark Shikari
15th July 2007, 06:28
A full, hopefully working package can be found here (http://www.mediafire.com/?1hb93gmtrmw) along with a README explaining how to use it.