View Full Version : Humbly requested: Filter SDK / Easy filter sample
Belgabor
9th March 2003, 23:59
This is kinda a bump on some facts in this old thread (http://forum.doom9.org/showthread.php?s=&threadid=40106&highlight=avisynth+sdk). So far I wasn't tempted to program a avisynth filter, but now I am ;)
The problem is I can't find an easy to understand sample source for an AviSynth filter, most sources are just too complicated to understand for a video filter programming newbie like me.
What I'm thinking of is a sample working in all colorspaces that manipulates the frame in some simple way (like drawing a centered square on it), perhaps in a future version samples for doing temporal stuff and an source filter.
I also would appreciate being pointed to already existing source code fulfilling this conditions (being simple, but for now YUY2 and YV12 would be enough for colorspaces).
Thanks in advance
Belgabor
If you can wait a few days- I'll write one for you.
I guarantee it will be simple :)
regards
Simon
Guest
10th March 2003, 00:37
I am so tempted to say "Simple Simon".
Isn't there the Invert sample at avisynth.org, or was it sh0dan's home page? That's where everyone starts. After that, there is SO MUCH source available for study.
Richard Berg
10th March 2003, 05:00
If you want to peek at the implementation of some of the core filters, I'd suggest starting with levels.cpp/h. All the colorspaces are demonstrated* and the algorithms are simple (lookup tables).
*Don't depend on these filters to learn everything, though. There are nuances like RGB being "upside-down" and YV12's alignment issues.
@neuron2 - I think the sample you're referring to is actually still on Ben's page.
trbarry
10th March 2003, 05:10
Belgabor -
Also see the Avisynth 2.5 alpha (http://cultact-server.novi.dk/kpo/avisynth/avisynth_alpha.html) page. This has a link to the converted Invert filter sample source.
- Tom
sh0dan
10th March 2003, 08:39
I put up some stuff (Ben's and my own) on the FilterSDK (http://www.avisynth.org/index.php?page=FilterSDK) page on avisynth.org - you are of course free to ask if you need further information.
Belgabor
10th March 2003, 12:41
Yay! Thanks so much guys. I will look into it when I get back home this evening :)
Here is first one. (available at
http://www.geocities.com/siwalters_uk/simplesample10.zip (please right-click to download from a geocities site) because I seem to have lost the ability/privliges to attach to either this or the Usuage forum :()
All it does is, well, nothing really.
It just takes the input and copies it to the output.
It doesn't work with YV12 but should work with all other colourspaces (cause its so simple :) )
The next one will put a square in the middle!
regards
Simon
sh0dan
11th March 2003, 00:12
Minor plea for change: Move colorspace checks to the constructor:
class SimpleSample : public GenericVideoFilter {
public:
SimpleSample(PClip _child) : GenericVideoFilter(_child);
}
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
};
SimpleSample(PClip _child) : GenericVideoFilter(_child) {
if (vi.IsPlanar()) // is input not planar
env->ThrowError("SimpleSample: input to filter must be in YUY2 or RGB");
}
(Also changed YV12 to planar, so that possible future planar formats also will fail). You should perhaps also do a planar version :)
RGB formats do actually work with the code you've made!
The planar implementation is actually quite easy - you can remove the colorspace check, and do:
[...]
const int dst_pitch = dst->GetPitch();
const int dst_width = dst->GetRowSize();
const int dst_height = dst->GetHeight();
const int src_pitch = src->GetPitch();
const int src_width = src->GetRowSize();
const int src_height = src->GetHeight();
int w, h;
//Copy src to dst
srcp = src->GetReadPtr();
dstp = dst->GetWritePtr();
for (h=0; h < src_height;h++) {
for (w = 0; w < src_width; w++)
*(dstp + w) = *(srcp + w);
srcp = srcp + src_pitch;
dstp = dstp + dst_pitch;
}
// end copy src to dst
const int dst_pitchUV = dst->GetPitch(PLANAR_U);
const int dst_widthUV = dst->GetRowSize(PLANAR_U);
const int dst_heightUV = dst->GetHeight(PLANAR_U);
const int src_pitchUV = src->GetPitch(PLANAR_U);
const int src_widthUV = src->GetRowSize(PLANAR_U);
const int src_heightUV = src->GetHeight(PLANAR_U);
//Copy U plane src to dst
srcp = src->GetReadPtr(PLANAR_U);
dstp = dst->GetWritePtr(PLANAR_U);
for (h=0; h < src_heightUV;h++) {
for (w = 0; w < src_widthUV; w++)
*(dstp + w) = *(srcp + w);
srcp = srcp + src_pitchUV;
dstp = dstp + dst_pitchUV;
}
// end copy src to dst
//Copy V plane src to dst
srcp = src->GetReadPtr(PLANAR_V);
dstp = dst->GetWritePtr(PLANAR_V);
for (h=0; h < src_heightUV;h++) {
for (w = 0; w < src_widthUV; w++)
*(dstp + w) = *(srcp + w);
srcp = srcp + src_pitchUV;
dstp = dstp + dst_pitchUV;
}
// end copy src to dst
[...]
As GetRowSize(PLANAR_U) will return 0 on non-planar images, nothing will be done in these loops, if the image is interleaved (RGB or YUY2).
@sh0dan
Whoa :)
Your code
class SimpleSample : public GenericVideoFilter {
public:
SimpleSample(PClip _child) : GenericVideoFilter(_child);
}
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
};
SimpleSample(PClip _child) : GenericVideoFilter(_child) {
if (vi.IsPlanar()) // is input not planar
env->ThrowError("SimpleSample: input to filter must be in YUY2 or RGB");
}
doesn't compile on my system.
Is it wrong? or is there an problem with my compiler.
I normally put the colourspace code "near the beginning" (my speak for somewhere in the constructor bit :) ) but it wouldn't work with the filter having no parameters :confused: . So I temporarily moved it.
I know it works with RGB :)
I'll stick your code in for YV12 in the next version.
sh0dan
11th March 2003, 08:32
Sorry - a but quick there - env was missing:
public:
SimpleSample(PClip _child, IScriptEnvironment* env);
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
};
SimpleSample::SimpleSample(PClip _child, IScriptEnvironment* env) : GenericVideoFilter(_child) {
if (vi.IsPlanar()) // is input not planar
env->ThrowError("SimpleSample: input to filter must be in YUY2 or RGB");
}
and
AVSValue __cdecl Create_SimpleSample(AVSValue args, void* user_data, IScriptEnvironment* env) {
return new SimpleSample(args[0].AsClip(),env);
}
You know when it is a contructor, because it is called the same as the class you are modifying, and it does return any values (not even void).
@sh0dan
Thanks for correction - maybe you could tell(PM if you want to) me me why we have to have 2 constructor definitions (as least it looks that way to me :confused: ) and why you can get away with 1 in other filters as long as it uses parameters :confused: :confused:
So a version 1.0a incorporating the correct colourspace checking now available at
http://www.geocities.com/siwalters_uk/simplesample10a.zip
regards
Simon
Acaila
11th March 2003, 10:41
Like Belgabor I've also wanted to try my hand at Avisynth filters now and then and like him I've also found it difficult to start because there are hardly any simple examples around.
Now the code that siwalters/sh0dan posted here looks simple enough, but I think what would be useful for a lot of newbie would-be filter writers are comments. And lots of 'em. Mainly why certain things are handled the way they are. Ben's original example is a good one but it only covers a very very small part of Avisynth's internal workings (and I bet a lot of it is outdated by now).
I for one am not very good at reading code and understanding what it does right away yet. So comments would help a lot in understanding.
sh0dan
11th March 2003, 11:45
I put in very detailed comments to the code.
SimpleSample 1.0b (http://cultact-server.novi.dk/kpo/avisynth/SimpleSample10b.zip).
No code changes - only thing changed is two redundant pointer requests where removed.
Acaila
11th March 2003, 12:02
Thank you, sh0dan. This helps a lot :)
@sh0dan
SimpleSample1.0b - Brilliant :D
Do you want to carry on or are you happy for me to do the basics and then you provide the professional touches?
regards
Simon
V1.1 now incorporates sh0dan's planar colourspace code.
Available here
http://www.geocities.com/siwalters_uk/simplesample11.zip
Changelog
Colourspace check removed from constructor as its not needed.
Minor changes to comments (mainly just highlighted the differences between pitch and width.
sh0dan
12th March 2003, 00:22
Nice - nothing to add from here :)
Wilbert
12th March 2003, 10:39
Available here (...)
Results in page not available?
sh0dan
12th March 2003, 11:47
Copy the URL into a new browser window.
Wilbert
12th March 2003, 11:56
:D Thanks!
Here is Version 1.2 which puts a white square in the middle of the frame - wow :)
It reverts to just handling RGB24 colourspace for simplicity.
http://www.geocities.com/siwalters_uk/simplesample12.zip
The next version will have variable parameters and then I'll add in the code for the other colourspaces.
regards
Simon
@Wilbert
Or just right-click and use Save Target As
Leuf
13th March 2003, 07:49
I've written quite a few vdub filters, and I've looked at the avisynth sdk a couple of times and what has me stumped is where are the equivalents to start/end proc that you have in vdub? An example that does some basic buffering would be helpful, specifically as to where should you allocate/free memory.
@Leuf
I asked a very similar question just a few days ago and the answer was that any static variables are defined in the constructor but I'm in the middle of porting my PFR filter and haven't yet debugged it so I can't tell whether I've got it right yet.
Once I have I'll point out how I did it :)
regards
Simon
sh0dan
13th March 2003, 10:35
Added destructor, and some member variables:
class SimpleSample : public GenericVideoFilter {
// SimpleSample defines the name of your filter class.
// This name is only used internally, and does not affect the name of your filter or similar.
// This filter extends GenericVideoFilter, which incorporates basic functionality.
// All functions present in the filter must also be present here.
public:
// This defines that these functions are present in your class.
// These functions must be that same as those actually implemented.
// Since the functions are "public" they are accessible to other classes.
// Otherwise they can only be called from functions within the class itself.
SimpleSample(PClip _child, IScriptEnvironment* env);
// This is the constructor. It does not return eny value, and is always used,
// when an instance of the class is created.
// Since there is no code in this, this is the definition.
~SimpleSample();
// The is the destructor. This is called when the filter is destroyed.
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
// This is the function that AviSynth calls to get a given frame.
// So when this functions gets called, the filter is supposed to return frame n.
private:
// This is where you put the class variables.
int box_width;
int box_height;
int box_x, box_y;
};
/***************************
* The following is the implementation
* of the defined functions.
***************************/
SimpleSample::SimpleSample(PClip _child, IScriptEnvironment* env) : GenericVideoFilter(_child) {
// This is the implementation of the constructor.
// The child clip (source clip) is inherited by the GenericVideoFilter,
// where the following variables gets defined:
// PClip child; // Contains the source clip.
// VideoInfo vi; // Contains videoinfo on the source clip.
if (!vi.IsRGB24()) // is input not RGB24
env->ThrowError("SimpleSample: input to filter must be in RGB24 colourspace");
}
SimpleSample::~SimpleSample() {
// This is where you can deallocate any memory you might have used.
}
Bidoche
13th March 2003, 10:52
Beware of the confusion between static variables (defined with the word static) and normal member variables.
static variables are kinda global variables, but in the namespace and scope of the class.
They are called static because they don't depend of the existence of class instances to exist.
They always exist and are unique, you write :
myClass::myStaticVar and not MyObject.myVar
You can use static in definition code too, it is used to define vars with global lifetime without worrying about polluting the :: namespace. (don't have to woory about choosing a smart name who won't be confused..)
exemple :
int CallCount() { //named as it does
static int count = 0; //this init is done once at the lodaing of the code
return ++count;
}
This could have been with count global but then it risk external modifications :/
Better programming to limit access to the good guy by putting it in the right place, that's the same idea with static var in class.
As for constructor/destructor its a symetric process.
Constructor builds members, create objects via new (new char[] for memory alloc generally).
Detructor destroys members (implicitly) and delete object created by new.
Btw implicit destuction is much safer :
if the constructor fails due to an exception, construction is ended, all members will be correctly destroyed, but pointers won't be deleted but leaked... :/
If you throw the exception yourself, you may explicitly destroy what must (but it's annoying and you may forget (ie one day you will)).
But if the exception is thrown by a called method (maybe you don't even know it can happen) you are ******.
To avoid that, one can use smart ptr class (who automatically delete the ptr they hold when leaving context) like std::auto_ptr
Edit: crossposted with sh0dan :p
jonny
13th March 2003, 11:04
I think this thread is great for programmers entering in the AviSynth development side for the first time (i'm one of these ^^')
What about make it sticky?
Here is SimpleSample 1.3
http://www.geocities.com/siwalters_uk/simplesample13.zip
It just adds in one parameter to vary the size of the square.
Syntax:
SimpleSample(clip,[size])
I intend to just plod on with now modifying it for extra colourspaces before trying to introduce any advanced stuff about global/static variables, buffering etc - remember, it is called SimpleSample ;)
@shOdan
I've added in the bits from your last post about the destructor but not the private member variables.
The code I've used to use a parameter is a combination of the code I normally use but modified in the light of what I'm learning from this thread about constructors. I'd welcome any comments (to put in a 1.3a version) and real comments to me about the way I've done it,which is just to copy the way I see others have done :o .
@Bidoche
Good information and one day I hope to understand more than the 10% of it that I do at the moment :o
Bidoche
14th March 2003, 12:13
@siwalters
Rome was not built in one day. :p
Btw, maybe you could precise that pitch must always be fetched after the read/write ptr.
I am not totally sure for 2.0/2.5 but in 3.0 a GetWritePtr may reallocate the picture and change the pitch.
sh0dan
14th March 2003, 19:22
Pitch only has to be refreshed after an env->NewVideoFrame().
GetWritePtr() cannot modify pitch in 2.0/2.5!
Oops Bugfix release :o
Version 1.3a available here
http://www.geocities.com/siwalters_uk/simplesample13a.zip
The change is from
for (w = (dst_width/2 - SquareSize/2)*3; w < (dst_width/2 + SquareSize/2)*3; w+=3) {
to
for (w = dst_width/2 - SquareSize*3/2; w < dst_width/2 + SquareSize*3/2; w+=3) {
The first version worked for some reason but proved to be wrong when I tried modifying it for use with RGB32 :o
regards
Simon
Version 1.4 available here
http://www.geocities.com/siwalters_uk/simplesample14.zip
Simply adds in RGB32 code.
regards
Simon
V1.5 with YUY2 colourspace code - 1 more to go :)
http://www.geocities.com/siwalters_uk/simplesample15.zip
regards
Simon
sh0dan
15th March 2003, 21:34
You could use vi->BytesFromPixels(1) instead of having separate loops for RGB24 and RGB32.
Much cleaner, and demonstrates more of the capabilities IMO.
vhelp
15th March 2003, 22:02
Hi siwalters.. sh0dan and Belgabor and others..
Just wanted to say THANKS for the idea of this thread. Something I've ben
looking for, for a long time.
siwalters, great idea and approach, how you build-on as you go. Nice and
slow. Great! Thanks.
Maybe some day, I'll return w/ a filter or too to share.
Please continue to keep up the good work.
-vhelp
@sh0dan
vi->BytesFromPixels(1) :confused:
So I went looking through the FilterSDK (http://www.avisynth.org/index.php?page=AviSynthTwoFiveSDK) until I found this (http://www.avisynth.org/index.php?page=WorkingWithImages) :)
Is this information/code new to 2.5 or has it always been around? :o
And therefore does it mean there is a GetRowWidth() function that can be called? :o :o :o
And could you give me/us an example of using vi->BytesFromPixels(1) please?
regards
Simon
Bidoche
16th March 2003, 12:23
// This will return the position of the upperleft pixel in YUY2 images,
// and return the lower-left pixel in RGB.
Arghhh... I didn't take this into account in the 3.0 code, and it won't be easily fixed (an ugly hack maybe)
Is it really vital ?
sh0dan
16th March 2003, 12:42
BytesFromPixels(int n) has always been around. Have a look at avisynth.h to see the funtions you can use.
Example (from fliphorizontal)
int bpp = vi.BytesFromPixels(1);
for (int y=0; y<h;y++) { // Loop for RGB and planar luma.
for (int x=0; x<row_size; x+=bpp) {
for (int i=0;i<bpp;i++) {
dstp[x+i] = srcp[-x+i];
}
}
srcp += src_pitch;
dstp += dst_pitch;
}
This is to avoid having separate loops for RGB24, RGB32 and YV12 (YUY2 is different due to the chroma placement).
@Bidoche: RGB is always flipped in AviSynth, and every filter processes it as such.
Bidoche
16th March 2003, 13:47
@sh0dan
Are you saying that it is necessary to be flipped, or that the code update cost will be too much ?
Acaila
16th March 2003, 15:36
Hi,
Something is not entirely clear to me in the following piece of code that deals with YUY2 colorspace.
dstp = dst->GetWritePtr(); // reset the destination pointer to the top, left pixel. (YUY2 colourspace only)
dstp = dstp + (dst_height/2 - SquareSize/2)*dst_pitch; // move pointer to SquareSize/2 lines from the middle of the frame;
int woffset = dst_width/8 - SquareSize/4; // lets precalulate the width offset like we do for the lines.
for (h=0; h < SquareSize;h++) { // only scan SquareSize number of lines
for (w = 0; w < SquareSize/2; w+=1) { // only scans the middle SquareSize pixels of a line
*((unsigned int *)dstp + woffset + w) = 0x80FB80FB; // Set Y1 and Y2 to max, U and V to no colour.
} // LSB = Y1, MSB = V
dstp = dstp + dst_pitch;
}
This is probably a newbie question, but then I am a newbie at coding so I hope you'll forgive me. According to this (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/YUVFormats.asp) page Y has a range of 16-235. However to make the square white you're setting it to FB (=251), why aren't you setting it to EB (=235) instead?
Just checking to see that your paying attention :o
(And it shows you can exceed the Y limits without causing an error ;))
Version 1.5a here
http://www.geocities.com/siwalters_uk/simplesample15a.zip
regards
Simon
sh0dan
16th March 2003, 20:37
@Bidoche: The data is flipped on input, and all RGB<->YUV conversion flip the data. So RGB is always flipped internally in AviSynth.
What I'm thinking of is a sample working in all colorspaces that manipulates the frame in some simple way (like drawing a centered square on it)
Done :D
Version 1.6 of SimpleSample here
http://www.geocities.com/siwalters_uk/simplesample16.zip
Thanks to everyone in helping me/us/we in getting there :)
@sh0dan
I didn't do anything with the vi.BytesFromPixels(1) as I haven't got my mind around it yet (being a bit slow and dim :) ) and I deliberately wanted to keep all colourspace code separate.
Now we've done the basic requirement do you (or someone else) want to optimise it?
Or add in some assembler, then MMX then SSE and all those other toyes you clever ones play with :)
regards
Simon
Guest
17th March 2003, 01:33
Originally posted by siwalters
Or add in some assembler, then MMX then SSE and all those other toys you clever ones play with :)It would then no longer be "SimpleSample". It seems to me you have reached the appropriate stopping point.
Bidoche
17th March 2003, 01:54
A bit unclear to have the four codepaths in one method.
Now maybe the time to replace those ifs by polymorphism
@sh0dan
we can reverse blit on input if we want, will be more consistent and I won't have to do this ***** hack
Richard Berg
17th March 2003, 04:38
@Bidoche -- that would break all filters that currently assume RGB is upside-down. Can you point out where the conflict arises in 3.0?
Bidoche
17th March 2003, 11:27
All GetReadPtr and GetWritePtr simply forward calls to BufferWindow methods of the same name (which returns top left corner).
Change cannot be down at BufferWindow level without ugliness (it doesn't have to know its RGB).
But it should work redefining GetWritePtr and GetReadPtr in the RGBVideoFrame class. (in videoframe.h)
sh0dan
17th March 2003, 18:15
I don't see the problem. The only difference is that the pointer points to lower-left of picture in "screenspace". Bufferwise it looks exactly like YUY2/YV12.
The picture is made the same way as all other - the only difference is that the lowest line is displayed as the upper line.
The pointer will still point to the first byte in the allocated array.
Bidoche
17th March 2003, 20:55
Hum, didn't understand it like that. So there is nothing to change.
And I have found a (minor) problem : if we add an alpha plane to each colorspace (besides rgb32), we won't be able to share it in the copy...
...perhaps in a future version samples for doing temporal stuff ...
Anyone fancy doing this bit then?
Simon
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.