View Full Version : "Superclip" objects idea - is this implementable, and where would I start?
wonkey_monkey
2nd February 2014, 19:32
Hi all, I've got yet another idea that slightly exceeds my knowledge of AviSynth's internals.
The idea is for "superclip" objects:
clip=avisource("source.avi")
s_clip=superclip(clip)
You could, for example, trim such a clip and get the trimmed video out:
s_clip.supertrim(50,150)
return s_clip.video
But the big difference with superclips is that you could also do things like this:
s_clip.untrim() # restore full length clip
s_clip.extend(50) # add another fifty frames to the end from the original clip
Because the trim state of the original clip is only stored in member variables it can be adjusted later.
Now, as I've said, I don't quite know AviSynth well enough yet to know if this is even possible, or where I'd start, so - could anyone give me some hints? :)
The only way I can think of at the moment is to add another audio track or extra video lines to store the data, but that doesn't seem very neat (and means the object is still just a clip as opposed to a new kind of object). Is it possible to create and operate on my own objects in this way?
Thanks!
David
ultim
4th February 2014, 19:55
You can achieve similar effect right now with much less code:
s_clip=clip
clip.trim(50,150)
s_clip.extend(50)
Or did I miss something?
wonkey_monkey
4th February 2014, 21:33
You can achieve similar effect right now with much less code:
s_clip=clip
clip.trim(50,150)
s_clip.extend(50)
Or did I miss something?
I think you might have:
Script error: there is no function named "extend"
raffriff42
5th February 2014, 00:22
I suppose some mashup of "hidden" globals and Eval statements could be made to do this, with enough work... *searching*
Hang on, AVSLib has "containers" based on this idea:
http://avslib.sourceforge.net/tutorials/containers.html
You could make an AVSLib "array" consisting of a clip and some properties such as its original framecount, its current trim in & out, its original size & current cropping, etc...
I don't know if Gavino's GrunT and/or StainlessS' RT_Stats can do this trick as well, but would not be surprised...
wonkey_monkey
5th February 2014, 11:17
That sounds like it might be what I need to do, thanks.
Container types are implemented as specially delimited strings.
A clip becomes part of a string? I suspect I have a lot of learning to do...
raffriff42
5th February 2014, 14:45
If I understand it correctly, ABSLib "arrays" can be thought of as user-defined types - they have data members, but not member functions as true objects do. So instead of s_clip.extend(50) you would call extend(s_clip, 50)
Gavino
5th February 2014, 16:27
instead of s_clip.extend(50) you would call extend(s_clip, 50)
In the Avisynth language, these are the same - the parser interprets s_clip.extend(50) as extend(s_clip, 50).
raffriff42
6th February 2014, 05:57
That's interesting Gavino, I had thought dot notation was for clip variables only, but you are correct as usual.
I went ahead and created a basic "clip object" using arrays for my own amusement, even though I can't see a compelling real-world case for such a thing. Maybe this code will serve as a starting point for something more interesting...#avisynth
LoadModule("avslib", "array", "core")
## set a delimiter that is not a valid path character
ArrayDelimiterSet("|")
#######################################
### basic clip object
### supports trim, untrim, crop and uncrop
###
### @raffriff42
### http://forum.doom9.org/showthread.php?t=170180
### http://avslib.sourceforge.net/tutorials/containers.html
###
### version 0.2.0 06-Feb-2014
### (added API ident, changed whenever updates break compatibility)
#######################################
#######################################
### create the clip object
##
## @ path - source path of clip C (optional; suggested usage only)
## @ tag - general purpose string (can be used for any purpose)
##
## NOTE: path & tag cannot contain the "|" (array delimiter) character.
##
function _CreateClipObject(clip C, String "path", String "tag")
{
path = Default(path, "")
tag = Default(tag, "")
Assert(FindStr(path, "|")==0, "_CreateClipObject: '|' not allowed in path")
Assert(FindStr(tag, "|")==0, "_CreateClipObject: '|' not allowed in tag")
return ArrayCreate( /* ID : DESCRIPTION */
\ "BCO-0.2" , /* 0 : object type and API ident */
\ C , /* 1 : underlying clip */
\ path , /* 2 : optional source path, user-supplied */
\ tag , /* 3 : general purpose tag, user-defined */
\ 0 , /* 4 : firstframe */
\ C.FrameCount , /* 5 : framecount */
\ 0 , /* 6 : leftcolumn */
\ C.Width , /* 7 : width */
\ 0 , /* 8 : toprow */
\ C.Height /* 9 : height */
\ )
}
#######################################
### return array index for a named property
##
function _i(String name)
{
return (name=="path") ? 2 :
\ (name=="tag") ? 3 :
\ (name=="firstframe") ? 4 :
\ (name=="framecount") ? 5 :
\ (name=="leftcolumn") ? 6 :
\ (name=="width") ? 7 :
\ (name=="toprow") ? 8 :
\ (name=="height") ? 9 :
\ Assert(false, "_i: invalid property name")
}
#######################################
### get clip in its current trimmed & cropped state
##
function _Clip(val o)
{
Assert(o.ArrayGet(0)=="BCO-0.2", "_Clip: object mismatch")
## debugging
#MessageClip(String(o._prop("height")))
#return PointResize(Width*16, Height*16)
return o.ArrayGet(1)
\ .Trim(o._prop("firstframe"), length=o._prop("framecount"))
\ .Crop(o._prop("leftcolumn"), o._prop("toprow"),
\ o._prop("width"), o._prop("height"))
}
#######################################
### return a named property for a clip object
##
function _prop(val o, String name)
{
Assert(o.ArrayGet(0)=="BCO-0.2", "_prop: object mismatch")
return o.ArrayGet(_i(name))
}
#######################################
### perform basic Trim on a clip object, relative to its current state
##
function _TrimRelative(val o, int "delta_start", int "delta_count")
{
Assert(o.ArrayGet(0)=="BCO-0.2", "_TrimRelative: object mismatch")
delta_start = Default(delta_start, 0)
delta_count = Default(delta_count, 0)
c = o.ArrayGet(1) ## underlying clip
cfirst = o._prop("firstframe")
ccount = o._prop("framecount")
newfirst = Min(Max(0, cfirst+delta_start), c.framecount-1)
newlast = Min(Max(newfirst, newfirst+ccount+delta_count), c.framecount)
o = o.ArraySet(_i("firstframe"), newfirst)
\ .ArraySet(_i("framecount"), newlast-newfirst)
return o
}
#######################################
### perform basic Crop on a clip object, relative to its current state
##
function _CropRelative(val o, int "delta_x", int "delta_y",
\ int "delta_w", int "delta_h")
{
Assert(o.ArrayGet(0)=="BCO-0.2", "_CropRelative: object mismatch")
delta_x = Default(delta_x, 0)
delta_y = Default(delta_y, 0)
delta_w = Default(delta_w, 0)
delta_h = Default(delta_h, 0)
c = o.ArrayGet(1) ## underlying clip
cx = o._prop("leftcolumn")
cy = o._prop("toprow")
cw = o._prop("width")
ch = o._prop("height")
new_x = Min(Max(0, cx+delta_x), c.width-1)
new_x2 = Min(Max(new_x+1, new_x+cw+delta_w), c.width)
new_y = Min(Max(0, cy+delta_y), c.height-1)
new_y2 = Min(Max(new_y+1, new_y+ch+delta_h), c.height)
o = o.ArraySet(_i("leftcolumn"), new_x)
\ .ArraySet(_i("width"), new_x2-new_x)
\ .ArraySet(_i("toprow"), new_y)
\ .ArraySet(_i("height"), new_y2-new_y)
return o
}
#######################################
### set clip object's "tag" property
## * a "tag" is a general purpose string (can be used for any purpose)
## * thin wrapper for 'o.ArraySet(_i("tag"), tag)'
## * if argument contains "|" (array delimiter), an error results.
##
function _set_tag(val o, String tag)
{
Assert(o.ArrayGet(0)=="BCO-0.2", "_set_tag: object mismatch")
Assert(FindStr(tag, "|")==0, "_set_tag: '|' not allowed")
o = o.ArraySet(_i("tag"), tag)
return o
}
##################### TESTS #####################
## RGB32, 1280x720, 29.97, 2576 frames
path = "D:\VideoProjects\work\port-imperial-01 huf.avi"
C = AviSource(path)
\ .ShowFrameNumber
o = _CreateClipObject(C, path)
#o = o._TrimRelative(10, -20) ## starts @ 10, shorter by 20
#return o._Clip.Info
o = o._TrimRelative(10, -20) ## starts @ 10, shorter by 20
o = o._TrimRelative(-5, +10) ## starts @ 5, shorter by 10
#return o._Clip.Info
#o = o._CropRelative(80, 80, 0, 0) ## 1200x640
#return o._Clip.Info
#o = o._CropRelative(80, 80, -160, -160) ## 1120x560
#return o._Clip.Info
#o = o._CropRelative(80, 80, 0, 0) ## 1200x640
#o = o._CropRelative(80, 80, -160, -160) ## 1040x480
#return o._Clip.Info
o = o._CropRelative(80, 80, 0, 0) ## 1200x640
o = o._CropRelative(80, 80, -160, -160) ## 1040x480
o = o._CropRelative(-120, -120, 120, 120) ## 1160x600
return o._Clip.Info (EDIT simply having "path" and "tag" properties available may be more useful than "un-trimming")
wonkey_monkey
7th February 2014, 00:01
Wow, thanks raffriff, I wasn't expecting to anyone to do my homework for me! ;)
I can't see a compelling real-world case for such a thing.
I thought I'd find it useful while editing, which is what I mainly use Avisynth for, as opposed to processing which most people seem to mainly use it for (see sig for shameless plug).
So I thought the concept could be useful for keeping scripts tidy, and also simplifying the application of some effects like dissolve. With "classic" Avisynth you need to calculate your overlap and trim accordingly, but with superclips you could trim your clips and use the same (super)clips with different lengths of dissolve or even a plain splice (subject to me writing super_dissolve and super_splice functions).
David
raffriff42
7th February 2014, 01:15
No problem, it was kinda my homework too; I didn't feel right about suggesting something without verifying it would actually work. I'm looking forward to seeing what you (and others?) do with it!
wonkey_monkey
7th February 2014, 18:57
For your consideration:
#######################################
### basic splice
function _Splice(val a, val b)
{
Assert(a.ArrayGet(0)=="BCO-0.2", "_Splice: object mismatch")
Assert(b.ArrayGet(0)=="BCO-0.2", "_Splice: object mismatch")
new_object = _CreateClipObject(\
a.ArrayGet(1).trim(0,a._prop("firstframe")+a._prop("framecount")-1)++\
b.ArrayGet(1).trim(b._prop("firstframe"),0)\
)\
.ArraySet(_i("firstframe"), a._prop("firstframe"))\
.ArraySet(_i("framecount"), a._prop("framecount")+b._prop("framecount"))
return new_object
}
Obviously anything beyond the trimmed end of a clip a and anything before the trimmed start of clip b is lost, but the two ends of the clip can be _RelativeTrimmed.
Regarding your initial code, personally I thought that storing lastframe instead of framecount would be more intuitive. I think it avoids more of those annoying "-1"s that have to be put into every trim, though I guess there may be just as many times when lastframe would be just as annoying.
I wish that Avisynth had been written with [inclusive start,exclusive end] as parameters to trim - to me it has never made sense that trim(100,200) returns a clip that is 101 frames long.
raffriff42
7th February 2014, 23:02
Regarding "lastframe," I agree. My (hasty) decision to use framecount was never supposed to be final in any way. Feel free to change things around; just use a new ident, eg "BCO-0.3"
Regarding the utility of it, I'm still not convinced, yet. To re-trim a clip now, it's easy to go back to the parent clip and trim it again. All we are adding here is a way to remember the pre-trimmed state, and that is available already if you have access to the parent clip - and you pretty much always will, unless you are writing a library function. Even then, would you really need access to frames before or after the clip you are working on?
On the other hand, maybe I'm being too short-sighted. These "super clips" (your term is better than "clip objects" - Avisynth clips are objects already) remind me of the things, usually also called clips, you have in a nonlinear editor: you can trim them, un-trim them, apply and remove effects, split them in two, clone them, join them into a sequence, overlay them, etc.
wonkey_monkey
7th February 2014, 23:23
All we are adding here is a way to remember the pre-trimmed state, and that is available already if you have access to the parent clip
Not quite - you're also adding the ability to extend the already-trimmed state. It may be that you have a clip, and you're happy with it, but somewhere else you might need the same clip but extended by a few frames - even if its just from an script-aesthetic point of view, it seems better to me that you could extend the trimmed clip rather than having to go back to the parent and recut it.
Even then, would you really need access to frames before or after the clip you are working on?
Often, if you're editing. Sometimes an audio edit is too harsh, and you need a couple of extra frames, faded, to paste on top of the next clip. Or there's the case of the dissolve.
Still, it's true, super clips probably throw up as many problems as they solve - as a neat concept they'd really have to built-in right at the start, filters written to accommodate them, etc.
Interesting exercise though!
Stereodude
8th February 2014, 02:14
even if its just from an script-aesthetic point of view, it seems better to me that you could extend the trimmed clip rather than having to go back to the parent and recut it.Because changing trim(26,9199) to trim(26,9199+50) is too hard?
wonkey_monkey
8th February 2014, 09:54
I'm not talking about just changing one clip's endpoints right at the start of a script.
You could have a need - and yes, these are somewhat artificial examples - to create two clips, both the same length, which are offset from each other by one second. Traditional Avisynth would require:
a=parent.trim(50,1999)
b=parent.trim(25,1924)
which is inefficient because you're basically repeating yourself, and means the script doesn't fully reflect the intent behind the code, a concept which I think has a name in programming, but which escapes me at the moment.
Instead I could do:
a=s_parent._trim(50,1999)
b=a._offset(-25)
Which means any script changes to a's trim are automatically reflected in b. When you're editing with a thousand-line Avisynth script, this kind of additional clarity of intention can be helpful (more helpful than using variables, for example).
Another example is one I mentioned earlier, when you have a clip but you need a little extra audio for pasting into another clip to soften the edit. Traditionally:
clip=parent.trim(123,456)
clip_=parent.trim(457,480) # extra audio
Super clip version:
clip=s_parent._trim(123,456)
clip_=clip._after(24)
I know the concept is useful to me, anyway.
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.