View Full Version : Crash using StackVertical
jsryork
1st June 2016, 09:36
Hi,
I have been working on an abstract animation moving cropped images around the screen.
I have encountered a crash:
Traceback (most recent call last):
File "avsp.pyo", line 8727, in OnMenuVideoNextFrame
File "avsp.pyo", line 14183, in ShowVideoOffset
File "avsp.pyo", line 13855, in ShowVideoFrame
File "avisynth.pyo", line 462, in GetFrame
WindowsError: exception: access violation writing 0x04B36000
Attached is a reduced example which shows the crash. I am using Avisynth.dll 2.6.0.6. I see the same effect with both Avspmod 2.5.1 or VirtualDub 1.10.5. I see the same effect on both my desktop (HP XW6600, 16Gb ram, Win7 Pro) and my laptop (HP G7, 6Gb ram, Win7 Home Premium).
The problem seems to relate to using StackVertical when I slide from one image into another vertically. Similar coding for a horizontal slide is OK. I have a 'safe' work-around not using StackVertical, but it is considerably slower. So I am not blocked on my project, but there is resumably an underlying issue which may give rise to other problems.
Thanks for a splendid piece of software.
John Robinson
Groucho2004
1st June 2016, 10:02
Post your complete script.
Edit: Don't attach the script, post it embedded in [code] tags.
jsryork
1st June 2016, 11:14
Sorry, here is the code:
# using avspmod 2.5.1
# using avisynth.dll 2.6.0.6
# using Windows 7 Pro, HP xw6600, 16Gb ram
# start of scene:psq_seq_0_tile_0
global bn_border = 1
# import ("..\common\my_funcs.avs")
global use_pc_range = false # scale RGB values
global fpbeat = 19.5 # frames per 'beat'
global fpbar = 39.0 # frames per 'bar'
global dest_width = 1920
global dest_height = 1080
global my_fps = 25
#Start sequence:psq_seq_0_tile_0
#Still_subimage:still_bg_si_0_0:1:1@0:0:932:932:fetching:
global p6mp_w = 2816
global p6mp_h = 2112
Function slide_clip_before_after_l_to_r(clip before, clip after, int start_frame, int move_frames)
{
return StackHorizontal(after, before).Animate(start_frame, start_frame + move_frames - 1, "Crop", before.width, 0, 0, 0, 0, 0, -after.width, 0)
}
Function slide_layer_t_to_b(clip before, clip after, float yfrac)
{
rv_clip = BlankClip(pixel_type = "RGB32", before.Framecount, width=before.width, height=before.height, color=$ffffff)
rv_clip = rv_clip.Layer(before.ResetMask, "add", 257, 0, Floor(before.height * yfrac))
return (yfrac <= 0.01) ? before : (yfrac >= 0.99) ? after : rv_clip.Layer(after.ResetMask.Crop(0, Floor(after.height * (1.0 - yfrac)), 0, 0), "add", 257, 0, 0)
}
Function slide_clip_before_after_t_to_b(clip before, clip after, int start_frame, int move_frames)
{
return StackVertical(after, before).Animate(start_frame, start_frame + move_frames - 1, "Crop", 0, before.height, 0, 0, 0, 0, 0, -after.height)
}
Function slide_clip_before_after_t_to_b_safe(clip before, clip after, int start_frame, int move_frames)
{
return Animate(start_frame, start_frame + move_frames - 1, "slide_layer_t_to_b", before, after, 0.0, before, after, 1.0)
}
white_1s = BlankClip(pixel_type = "RGB32", my_fps, width=dest_width, height=dest_height, fps=my_fps, color=$ffffff, stereo = true)
still_6mp_red = BlankClip(pixel_type = "RGB32", 1, width=p6mp_w, height=p6mp_h, fps=my_fps, color=$AA0000)
still_6mp_red_br = still_6mp_red.BilinearResize(932, 932, 480, 60, 1941, 1941 )
d_39 = still_6mp_red_br.Loop(39)
still_6mp_blue = BlankClip(pixel_type = "RGB32", 1, width=p6mp_w, height=p6mp_h, fps=my_fps, color=$0000BB)
still_6mp_blue_br = still_6mp_blue.BilinearResize(932, 932, 480, 60, 1941, 1941 )
v_39 = still_6mp_blue_br.Loop(39)
block_0_0_0_0_2_tb = slide_clip_before_after_t_to_b(d_39, v_39, 4, 24).AddBorders(bn_border, bn_border, bn_border, bn_border, $000000).ResetMask
block_0_0_0_0_2_lr = slide_clip_before_after_l_to_r(d_39, v_39, 4, 24).AddBorders(bn_border, bn_border, bn_border, bn_border, $000000).ResetMask
block_0_0_0_0_2_tb_safe = slide_clip_before_after_t_to_b_safe(d_39, v_39, 4, 24).AddBorders(bn_border, bn_border, bn_border, bn_border, $000000).ResetMask
# show preview in Avspmod, step from frame 4 to 5, with following results
return block_0_0_0_0_2_tb # error : WindowsError: exception: access violation writing 0x0C066000
return block_0_0_0_0_2_lr # ok
return block_0_0_0_0_2_tb_safe # ok
Groucho2004
1st June 2016, 11:57
I'm no avs debugging specialist like StainlessS but it seems that "-after.height" in "slide_clip_before_after_t_to_b" is the culprit and returns an invalid value at frame #5.
Gavino
1st June 2016, 17:16
The problem arises from applying Animate() to Crop(), varying both the top and bottom parameters in parallel (from (932, 0) at frame 4 to (0, -932) at frame 27).
Because the variation in these parameters over the animation (932) is not a multiple of the number of animation steps (23), truncation after integer division occurs when calculating the intermediate values at each frame.
Since top is positive and bottom is negative, the truncation affects them differently, so they don't actually move precisely in parallel - the values at frame 5 are (891, -40) and the height of the resulting Crop is one pixel greater than it should be (933 instead of 932).
This produces an access violation when the unexpectedly larger frame is written. (Animate() checks that the dimensions of the starting and ending frames match, but not the intermediate ones.)
You can fix this by animating only the top parameter and explicitly setting the height to a constant value.
... Animate(..., "Crop", 0, before.height, 0, after.height, 0, 0, 0, after.height)
(Alternatively, animate a resizer instead of Crop(), and use the floating point arguments src_top etc to get a smoothly varying change)
StainlessS
1st June 2016, 17:52
Arh, beaten by some visitor :(
Like what G said,
decomposed
Function slide_clip_before_after_t_to_b(clip before, clip after, int start_frame, int move_frames) {
cc=StackVertical(after, before)
### Animate (clip, int start_frame, int end_frame, string filtername, start_args, end_args)
cc.Animate(start_frame, start_frame + move_frames - 1,
\ "Crop",
\ 0, before.height, 0, 0,
\ 0, 0, 0, -after.height)
return Last
}
red = BlankClip(length=39,width=932,height=932,pixel_type="RGB32",fps=25,color=$AA0000)
blue = red.BlankClip(color=$0000BB)
# EXCEPTION BELOW WHEN ADDBORDERS
return slide_clip_before_after_t_to_b(red,blue, 4, 24) #.AddBorders(1,1,1,1,0)
alternative keeping crop widths and height same (result MUST be same, clip size cannot vary)
Function slide_clip_before_after_t_to_b(clip before, clip after, int start_frame, int move_frames) {
cc=StackVertical(after, before)
cc.Animate(start_frame, start_frame + move_frames - 1,
\ "Crop",
\ 0, before.height, Before.Width, Before.height,
\ 0, 0, Before.Width, Before.height)
return Last
}
red = BlankClip(length=39,width=932,height=932,pixel_type="RGB32",fps=25,color=$AA0000)
blue = red.BlankClip(color=$0000BB)
return slide_clip_before_after_t_to_b(red,blue, 4, 24).AddBorders(1,1,1,1,0)
EDIT: G2004, you flatter me :)
EDIT: These are numbers I figure (rel start frame [float calc, addition at each stage, loses a bit of precision <done quick>])
00000004 17:35:30 RT_DebugF: 0 ] 932.000000 0.000000
00000005 17:35:30 RT_DebugF: 1 ] 891.478271 -40.521740
00000006 17:35:30 RT_DebugF: 2 ] 850.956543 -81.043480
00000007 17:35:30 RT_DebugF: 3 ] 810.434814 -121.565216
00000008 17:35:30 RT_DebugF: 4 ] 769.913086 -162.086960
00000009 17:35:30 RT_DebugF: 5 ] 729.391357 -202.608704
00000010 17:35:30 RT_DebugF: 6 ] 688.869629 -243.130447
00000011 17:35:30 RT_DebugF: 7 ] 648.347900 -283.652191
00000012 17:35:30 RT_DebugF: 8 ] 607.826172 -324.173920
00000013 17:35:30 RT_DebugF: 9 ] 567.304443 -364.695648
00000014 17:35:30 RT_DebugF: 10 ] 526.782715 -405.217377
00000015 17:35:30 RT_DebugF: 11 ] 486.260986 -445.739105
00000016 17:35:30 RT_DebugF: 12 ] 445.739258 -486.260834
00000017 17:35:30 RT_DebugF: 13 ] 405.217529 -526.782593
00000018 17:35:30 RT_DebugF: 14 ] 364.695801 -567.304321
00000019 17:35:30 RT_DebugF: 15 ] 324.174072 -607.826050
00000020 17:35:30 RT_DebugF: 16 ] 283.652344 -648.347778
00000021 17:35:30 RT_DebugF: 17 ] 243.130600 -688.869507
00000022 17:35:30 RT_DebugF: 18 ] 202.608856 -729.391235
00000023 17:35:30 RT_DebugF: 19 ] 162.087112 -769.912964
00000024 17:35:30 RT_DebugF: 20 ] 121.565369 -810.434692
00000025 17:35:30 RT_DebugF: 21 ] 81.043625 -850.956421
00000026 17:35:30 RT_DebugF: 22 ] 40.521885 -891.478149
00000027 17:35:30 RT_DebugF: 23 ] 0.000145 -931.999878
EDIT: But of course nicely smoother less jittery if using resize instead of crop.
EDIT; Like so
Function slide_clip_before_after_t_to_b(clip before, clip after, int start_frame, int move_frames) {
w=before.width h=before.height
cc=StackVertical(after, before)
cc.Animate(start_frame, start_frame + move_frames - 1,
\ "Spline36Resize",
\ w, h, 0, h, w, h,
\ w, h, 0, 0.0, w, h)
return Last
}
red = BlankClip(length=39,width=932,height=932,pixel_type="RGB32",fps=25,color=$AA0000)
blue = red.BlankClip(color=$0000BB)
return slide_clip_before_after_t_to_b(red,blue, 4, 24).AddBorders(1,1,1,1,0)
Although jumps are so big as to make little difference to smoothness, but probably better method to adopt in general.
Gavino
1st June 2016, 20:20
cc.Animate(start_frame, start_frame + move_frames - 1,
\ "Spline36Resize",
\ w, h, 0, h, w, h,
\ w, h, 0, 0.0, w, h)
It's worth emphasising that you must use that 0.0 there (for the varying argument) and not just plain 0
Otherwise, the animation will use integer interpolation and you won't get a smooth variation.
StainlessS
1st June 2016, 20:35
Yep thank you G, that was an afterthought addition (initially missed it myself), perhaps I should have made the importance more explicit.
I am though unsure as to why the original error reveals itself at AddBorders() statement (bug ?, exception needs investigation).
EDIT: Just checked with AVS+ (r1576) and also has exception.
Gavino
1st June 2016, 23:16
I am though unsure as to why the original error reveals itself at AddBorders() statement (bug ?, exception needs investigation).
AddBorders() is getting its input frames from Animate(), the previous filter in the chain.
For frame 5 (in the example above) it receives a frame that is one row bigger than it was told by Animate() to expect, so it writes beyond the end of its frame buffer when constructing its output data.
Is it a bug?
Well, the original error is in the script itself (erroneous argument combination to Animate()), but it would be better if it was detected and reported gracefully instead of crashing.
However, it cannot be detected until run-time, so it's not clear what action could be taken.
jsryork
2nd June 2016, 09:51
Thanks to all for your prompt explanation and solutions.
I am still a bit puzzled as to why the StackHorizontal was OK.
But now I can use the faster coding (I will check if I can see a speed difference between animating Crop or a Resizer). I use similar coding in some other areas (scrolling credits, for instance), so I will update those for a floating-point interpolation, to see if they appear smoother.
Great support, thanks guys.
John
Gavino
2nd June 2016, 11:26
I am still a bit puzzled as to why the StackHorizontal was OK.
The horizontal case still goes wrong since, on the first frame into the animation, the Crop() produces a frame that is one pixel wider than expected.
However, in this case AddBorders() doesn't crash - the way the code happens to be written, the extra pixel just replaces the right-hand border and you (sort of) get away with it.
StainlessS
3rd June 2016, 18:10
and you (sort of) get away with it.
There is a similar-ish "sort of get away with it" in C.
Many noobie C/CPP proggers think that its OK to not add 1 to strlen() when allocating a string buffer (for the nul term sentinel).
Reason being that for small strings [strlen() 7 or less], you also "sort of get away with it".
You get away with it because the library functions (malloc/new) allocate in blocks of 8 bytes. 8 byte block size due to
optimizations in the library code and also because malloc pointer has to be castable to a pointer of any type (including alignment).
Optimization reasons include, it is more efficient to have minimum size allocated as 8 bytes as this reduces memory fragmentation
and does away with extended search times looking for a free memory block that is big enough to satisfy a malloc request.
A second optimization is that a free memory block uses it's OWN memory (1st 8 bytes) to store its size (4 bytes) and a pointer
to the next free memory block (4 bytes).
So, a noobie progger will get away with it when requesting a string of eg 7 bytes length when 8 are actually required, as 8 bytes are actually
given, (only 7 of which belong to the caller). The problem arises when noobie requests 8 bytes for strlen()==8 string and 9 bytes
are actually required, malloc will in such case only carve off 8 bytes, just as requested, and bang!!! noobie got a problem.
For 64 bit malloc/new, it is likely that minimum block size will be 16 bytes, giving noobies even more confidence that they are doing it right.
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.