View Full Version : Baked code removal - Proposal for AviSynth 2.6
IanB
23rd October 2005, 10:42
Dear all,
I have been tinkering around with the aim to remove all the baked in code from avisynth.h and I have arrived at an impasse.
How do I make the currently baked code available to pluggins in such a way that other server environments may respecify the provided code to suit there own need.
Specifically Avisynth 3.0 and VirtualDub [Upcoming new API version].
My initial approach was to __declspec(dllexport/dllimport) all the relevant routines. This works fine on the surface, BUT!
Pros :
Pluggin sources are 100% compatible.
Achives goal for avisynth.dll as server.
Cons :
Need to link with an avisynth.lib (not much of a con)
Pluggins are staticly bound to a file called avisynth.dll
Alternate servers cannot respecify provided code. (very disappointing)
One thought I had was to provide a pluggin.lib instead of an avisynth.lib which provides stub routines that indirectly call the appropriate code and a mechanism to initialize the function table with the entry points for an alternate server.
Anyway before I type to much code in I thought some brain storming on the forum might elicit a cleaner approach.
Let the ideas flow :sly:
IanB
Guest
23rd October 2005, 14:55
Please define "baked code" and explain why it is an issue. We're not all intimately connected with your thinking. :)
tsp
23rd October 2005, 15:59
http://forum.doom9.org/showthread.php?p=707713#post707713
one of the problems with baked code in this case is to implement something like multithreading without using all sort of ugly hacks
IanB
23rd October 2005, 16:21
Baked code is code that is included inline. In this case all the code in avisynth.h. The result is all that code is "baked" permanently into each and every plugin instead being called from the current avisynth.dll.
The problem with baked in code is that it cannot be changed without recompiling the plugin. If it were just a declaration only then the active code would be whatever is in the current avisynth.dll.
So the idea in getting rid of the avisynth.h code from the pluggins would avoid the need to recompile the pluggins again in the future. Wanting to keep 2.5 pluggin compatibilty in 2.6 imposes some restriction. e.g. The code for calculating UVPitch of YV12 images in every pluggin calculates it as YPitch/2. For the new planar formats UVPitch is now independant of YPitch but for YV12 this is not possible.
d'Oursse
24th October 2005, 09:25
I'm not an avisynth expert, but here are my 2 cents:
1) avisynth.h is the exported objects and functions, that is the api, and only the api
2) another file(s) like avisynth_private.h (or avisynth_private_convert.h, avisynth_private_resize.h, etc...) that could contain inlined functions.
At least, that would reduce a bit the problem.
Imho, the 1st point is essential.
mg262
24th October 2005, 10:44
This is tangential to the main question, but I think it is still worth saying...it might be worth having the classes in AVISynth.h as wrappers around pointers to abstract interface classes (classes with only pure virtual functions). Like this:
class Implementation*
{
public:
virtual void firstmethod() = 0;
virtual int secondmethod() = 0;
};
class MainClass
{
Implementation *implementation;
public:
MainClass(Implementation *_implementation)
: implementation(_implementation) {}
void firstmethod() {implementation->firstmethod();}
int secondmethod() {return implementation->secondmethod();}
};*It's perhaps more appropriate to have MainClass::Implementation rather than have it as a separate class.
... also of course functions that return objects of type MainClass. Then the private code never needs to appear in publicly visible header files at all.
MfA
24th October 2005, 14:24
For the moment adding a private pointer is out of the question though, since it destroys binary compatibility. Once you can add one there is no need to build that much indirection in at the start, without inlined code and if you have something like a d-pointer (http://developer.kde.org/documentation/other/binarycompatibility.html) then if it's really necessary you can always do that after the fact. (You make the d-pointer point to the new implementation, and have all the methods call their counterparts in that new object). Putting all that indirection and extra lines of code in ahead of time is needless.
Ian, could you give an example of why other servers would want to "respecify the provided code to suit there own need."? If avisynth can request frames from them, and provide them with the processed output wouldnt that be enough?
IanB
24th October 2005, 14:56
@d'Oursse,
The aim here is make avisynth.h purely definition so that pluggins have no resident avisynth code, all such code is provided by the server, that being avisynth.dll or virtualdub (a future version) or something else.
Current 2.5 API pluggins have code included that is frankly quite inconvenient and limiting from an enhancment point of view.
> ... that could contain inlined functions.
This is precisely what we do not want. We want the reference to such code transparently linked back into the current server.
@mg262,
This is pretty much the way IScriptEnvironment is implemented. The functions provided thru this partial interface are fully transparent and can be whatever the current server need to provide a succesfull hosting of the pluggin. This is a very good thing.
Unfortunatly many other parts of the interface, for example AVSValue or VideoInfo are not implemented this way. The code for these classes are included in all pluggins. It is "Baked" code.
Now the problem with virtual classes is you cannot instantiate an instance of that explicit class you have to instantiate a different class that implements the virtual class. Thus source code like :
VideoInfo vi;
would have to become
VideoInfo *vi = new IVideoInfo();
And the argument becomes circular at this point because you still need to link to class IVideoInfo so you might as well directly implement VideoInfo and link to that.
Hence this thread, I want a clean way to transparently link the code for the baked classes in avisynth.h to the server loading the pluggin, not nessecarily avisynth.dll.
IanB
IanB
24th October 2005, 15:50
@MfA,
A point of clarification :- "Server" here means something that loads and executes an avisynth.h api pluggin.
This is normally avisynth.dll but Avery Lee has expressed a desire to be able to load and execute avisynth pluggins directly in VirtualDub. Much the same way Avisynth can currently load VirtualDub pluggins today. And Avisynth 3.0 is a complete rewrite. For example planar video data is stored in 3 independant buffers that can be independantly manipulated.
An example :-
Class VideoFrame is well implemented, all of the data is private, all of the access to that data is thru well defined methods. Unfortunatly the implementation of those methods is exposed and locked down. So to load and execute a pluggin a server has to emulate an awful lot of avisynth data structures to satisfy this baked in code, probably having to blit the video data into and back out of a pseudo VideoFrameBuffer. If the code for these methods was linked back into the server then the emulation environment could be greatly simplified. A reimplemented GetReadPtr() could return a pointer to the native frame buffer memory.
IanB
mg262
24th October 2005, 16:16
MfA, point taken about an extra private member. I didn't put it correctly, but I meant to make a point about separating interface and implementation... really just meant that as a note on separating interface and implementation properly in response to d'Oursse's second point -- but as youand Ian pointed out, it causes problems with the existing material. I have to confess that I don't understand how any changes can be made completely safely -- it seems to me that the compiler/linker are perfectly free to reorder or align data structures if they chooses to. (Perhaps since this kind of optimisation crosses object files it is too complex to happen in practice?)
Ian,
How do we currently load version 2.0 plug-ins?
MfA
25th October 2005, 05:14
MfA, point taken about an extra private member. I didn't put it correctly, but I meant to make a point about separating interface and implementationThe moment you don't use inlined code that is already achieved though (although changing the implementation without a d-pointer can be a bit of a pain). You don't need virtual functions for that.
Im not sure if I see the value of allowing external programs to override internal functions. Overriding GetReadPtr sounds okay, but lets take the proposed change to let filters request aligned buffers ... can you truely say you can foresee how such internal changes will interact with overridden methods? Having virtual functions which can be overriden ties your hands in changing the implementation of the class almost as much as inlined code does AFAICS.
Any program which wants to dig that deeply into the avisynth plugin environment would be better off branching the code IMO. You don't really need to go that far to be able to get zero copy input of frames at any rate, the ways in which they can be stored in memory is strictly limited by the way filters access them ... if videoframebuffer was reimplemented to make less assumptions about how exactly it is stored then zero copy input would be straightforward. Nothing needs to be overridden for that.
IanB
25th October 2005, 08:28
@mg262,
> how any changes can be made completely safely
You are right in principle. However Avisynth is restricted to the Microsoft compiler. Now this is a multi-edged sword. The bad things are like people grizzle because they can't compile pluggins with GCC, etc. The good things are like we can extend classes and structures and get away with it, such that previous binary versions do not notice.
> How do we currently load version 2.0 plug-ins?
A case in point. Somebody has written an interface layer and it works very hard to do the job and not always successfully. [[ Has anyone got a copy of the source for this? ]]
@MfA,
> Im not sure if I see the value of allowing external programs to
> override internal functions.
You are looking at this from the wrong perspective. They are not external programs overiding internal functions. They are applications implementing the server side of the avisynth.h API so they can load and execute Avisynth pluggins.
> ... don't really need to go that far to be able to get zero copy
> input of frames at ...
True, there are probably only a half dozen instance where having the code baked is a serious annoyance, but the effort to make these opaque is the same to make all the baked code opaque.
@All,
As I said in my opening post I want ideas on how to move the baked code from avisynth.h into the server. So far we have a refernce to d-pointer and this is not quite on the mark but in the general area.
I can simply use __declspec(dllimport/export) and all Sh0dan's and my immediate and future problems are solved. But I am a bit more public spirited than that, I want the linkage to be to the current server not just avisynth.dll
IanB
foxyshadis
25th October 2005, 08:50
The problem is that if you want to create a restricted COM environment, without using COM... you just have to reinvent COM. (This applies for any of the distributed interprocess protocols, COM is just the best-known on windows.) Obviously you don't need the vast majority of the framework, mostly server discovery and some other details, so maybe it's a good idea to check out the different standards in this area and see which one looks most workable, then implement a cut-down version of it.
Marshalling might be a good thing, let anyone use any compiler, but whomever created the marshalling layer is in for a huge amount of work, so probably not.
Wilbert
25th October 2005, 09:35
A case in point. Somebody has written an interface layer and it works very hard to do the job and not always successfully. [[ Has anyone got a copy of the source for this? ]]
http://www.avisynth.org/warpenterprises: WarpSharp package.
You need LoadPluginEx1.cpp and LoadPluginEx2.cpp. I think the former is to open v2.5 plugins in v2.0 and the latter to open v2.0 plugins in v2.5.
MfA
25th October 2005, 09:41
Any alternate server could always just put their own avisynth.dll in it's own directory ;)
IanB
25th October 2005, 09:52
@foxyshadis, COM, bite your tongue. Restricted marshalling, a fallback position.
@Wilbert, Ta!
@MfA, YUCK! :devil:
MfA
25th October 2005, 11:41
AFAICS, stubs are pretty much the only way then ...
I assume you were planning to add a new function to the script environment to set the name of the server dll? How to get this information to the code which has to initialize the function pointers for the stubs though? Don't think you could do it in env->AddFunction, and the GenericVideoFilter constructor is usually called next ... with no access to the script environment :/
IanB
25th October 2005, 16:15
@MfA,
Oooo 2 edits^h^h^h^h^hrewrites.
> ...stubs...
Which is pretty much the conclusion I came to. So I came here to see if somebody might have a clevererer idea.
> ... new function to the script environment to set the name of the server dll?
Still missing the point, the idea is for something other than avisynth.dll to load and run the pluggin. The server is the thing that loaded the pluggin and called the AvisynthPluginInit3 entry point
IanB
MfA
25th October 2005, 18:30
Still missing the point, the idea is for something other than avisynth.dll to load and run the pluggin. The server is the thing that loaded the pluggin and called the AvisynthPluginInit3 entry pointI thought you wanted to implement it such that filters could be simply be recompiled to be compatible with this new approach ... which presents the problem of how exactly the server is going to tell the plugin the location of the functions. AddFunction can not get access to necessary datastructures in the plugin's memory, and some of the first presently inlined functions which get called don't have access to the obvious place to share this informatiom ... the script environment. It could be done, but if you are willing to make a completely new plugininit for this just passing a pointer to an array of function pointers is a lot easier I guess.
In the end you don't need a plugin.lib or use dllexport/import. Just move the implementation of the functions to avisynth.cpp, and include stubs in avisynth.h for conditional compilation by plugins.
IanB
26th October 2005, 04:20
Hmm, baked code that is only stubs. A baked AvisynthPluginInit3 that loads the pointers, then maybe calls AvisynthPluginInit2. I think you maybe on to something.
Bidoche
26th October 2005, 22:52
> Im not sure if I see the value of allowing external programs to
> override internal functions.
You are looking at this from the wrong perspective. They are not external programs overiding internal functions. They are applications implementing the server side of the avisynth.h API so they can load and execute Avisynth pluggins.Indeed.
It can be quite convenient for vdub to be able to provide avs3 frames backed by its own memory model.
Bidoche
26th October 2005, 23:26
Hmm, baked code that is only stubs. A baked AvisynthPluginInit3 that loads the pointers, then maybe calls AvisynthPluginInit2. I think you maybe on to something.AvisynthPluginInit3(AvisynthServerContext& context)
class AvisynthServerContext
{
//virtual functions ...
};
NB: it's a suggestion, not the current scheme
MfA
27th October 2005, 02:10
An array of function pointers can be extended without relying on the compiler to not mess with the order of virtual functions in a virtual function table ...
With a function pointer you can simply call *this.*functionPointer in the stub, clean enough.
Bidoche
27th October 2005, 07:55
Why would the compiler mess with functions order ?
Besides I kinda need it to be an object, parser contexts have state.
MfA
27th October 2005, 08:40
They dont necessarily have to be stored in order of declaration, hell ... they dont even need to be stored the same between different compilers. Avisynth is only portable between different versions of VC because Microsoft does it's utmost best not to break hacks.
Parser contexts might have state, but I dont see the relevance to the plugin ... all the stubs have to do is call the correct function, why do they need state to do that?
IanB
27th October 2005, 12:15
Keep the ideas rolling guys, the light in my head is still not quite at 110% yet. :D
Even silly ideas can prompt serendipity.
Bidoche
27th October 2005, 12:46
They dont necessarily have to be stored in order of declaration, hell ... they dont even need to be stored the same between different compilers. Avisynth is only portable between different versions of VC because Microsoft does it's utmost best not to break hacks.Unless we reinvent COM, we won't be able to get compiler compatibilities, ever...
Parser contexts might have state, but I dont see the relevance to the plugin ... all the stubs have to do is call the correct function, why do they need state to do that?Functions that alter state (functions registration essentially) need the state to work
MfA
27th October 2005, 19:19
Or use an OO C API and wrap it with C++.
IScriptEnvironment is already all virtual, this is about the inlined functions. Using a class with virtual methods to handle all the inlined methods would require an extra layer of thunking AFAICS. You'd have to include the this pointer as an explicit parameter and then inside the server have another stud to call the actual method on it.
IanB
3rd November 2005, 09:47
Dear All,
After much thought and soul searching, I find myself pretty much back where I started, albeit cleaned up a bit.
Here is the break out as I envision it. Your thoughts and comments please.//------------------------
// plugin.h
//------------------------
class PluginLinkage {
public:
virtual bool viHasVideo(const VideoInfo *That);
virtual bool viHasAudio(const VideoInfo *That);
...
virtual bool viIsSameColorspace(const VideoInfo *That, const VideoInfo &vi);
...
}//------------------------
// plugin.lib side
//------------------------
// RemotePlugin.cpp
static PluginLinkage *Gvectors = 0; // or &LocalLinkage;
extern "C" const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* env);
extern "C" __declspec(dllexport) const char* __stdcall
AvisynthPluginInit3(IScriptEnvironment* env, PluginLinkage *vectors)
{
Gvectors = vectors;
return AvisynthPluginInit2(env); // User provided
}
// Interlude stubs
bool VideoInfo::HasVideo() const { return (Gvectors->viHasVideo(this)); }
bool VideoInfo::HasAudio() const { return (Gvectors->viHasAudio(this)); }
...
bool VideoInfo::IsSameColorspace(const VideoInfo& vi) const {
return (Gvectors->viIsSameColorspace(this, vi));
}
... //------------------------
// avisynth.dll side
//------------------------
// LoadPlugin.cpp
static PluginLinkage vectors; // Need an object to get the vtable
...
// Init the plugin
title = *AvisynthPluginInit3(env, &vectors);
...
//------------------------
// LocalPlugin.cpp
bool PluginLinkage::viHasVideo(const VideoInfo *That) { return That->HasVideo(); }
bool PluginLinkage::viHasAudio(const VideoInfo *That) { return That->HasAudio(); }
...
bool PluginLinkage::viIsSameColorspace(const VideoInfo *That, const VideoInfo &vi) {
return That->IsSameColorspace(vi);
}
//------------------------
// VideoInfo.cpp
bool VideoInfo::HasVideo() const { return (width!=0); }
bool VideoInfo::HasAudio() const { return (audio_samples_per_second!=0); }
...
bool VideoInfo::IsSameColorspace(const VideoInfo& vi) const {
if (vi.pixel_type == pixel_type) return TRUE;
if (IsYV12() && vi.IsYV12()) return TRUE;
return FALSE;
}//------------------------
// USER_PLUGIN.cpp side
//------------------------
extern "C" __declspec(dllexport) const char* __stdcall
AvisynthPluginInit2(IScriptEnvironment* env)
{
env->AddFunction("Plugin", "c", Create_Plugin, 0);
return "Plugin";
}//------------------------
// avisynth.h
//------------------------
...
struct VideoInfo {
int width, height; // width=0 means no video
unsigned fps_numerator, fps_denominator;
...
// useful functions of the above
bool HasVideo() const;
bool HasAudio() const;
...
bool IsSameColorspace(const VideoInfo& vi) const;
};
...IanB
MfA
3rd November 2005, 18:26
So in the end what was wrong with ...
//------------------------
// avisynth.h
//------------------------
...
struct VideoInfo {
int width, height; // width=0 means no video
unsigned fps_numerator, fps_denominator;
...
// useful functions of the above
bool HasVideo() const;
bool HasAudio() const;
...
bool IsSameColorspace(const VideoInfo& vi) const;
};
...
struct ServerFunctions {
...
bool (*viHasVideo)() const;
bool (*viHasAudio)() const;
...
bool (*viIsSameColorspace)(const VideoInfo &vi);
...
}
...
#ifdef __AVISYNTH_CORE__
ServerFunctions* InitServerFunctions()
{
ServerFunctions *serverFunctions;
serverFunctions = new ServerFunctions;
...
serverFunctions->HasVideo = &VideoInfo.HasVideo;
serverFunctions->HasAudio = &VideoInfo.HasAudio;
serverFunctions->IsSameColorSpace = &VideoInfo.IsSameColorSpace;
...
return serverFunctions;
}
#else //__AVISYNTH_CORE__
ServerFunctions *serverFunctions;
extern "C" __declspec(dllexport) const char* __stdcall
AvisynthPluginInit3(IScriptEnvironment* env, ServerFunctions *vectors)
{
serverFunctions = vectors;
return AvisynthPluginInit2(env); // User provided
}
...
bool VideoInfo::HasVideo() const { return *this.*(serverFunctions->HasVideo)(); }
bool VideoInfo::HasAudio() const { return *this.*(serverFunctions->HasAudio)(); }
...
bool VideoInfo::IsSameColorspace(const VideoInfo& vi) const
{
return *this.*(serverFunctions->IsSameColorspace)(const VideoInfo& vi);
}
...
#endif //__AVISYNTH_CORE__
//------------------------
// avisynth.cpp
...
bool VideoInfo::HasVideo() const { return (width!=0); }
bool VideoInfo::HasAudio() const { return (audio_samples_per_second!=0); }
...
bool VideoInfo::IsSameColorspace(const VideoInfo& vi) const {
if (vi.pixel_type == pixel_type) return TRUE;
if (IsYV12() && vi.IsYV12()) return TRUE;
return FALSE;
}
//------------------------
// plugin.cpp
//------------------------
...
AvisynthPluginInitFunc AvisynthPluginInit =
(AvisynthPluginInitFunc)GetProcAddress(plugin, "AvisynthPluginInit3");
...
AvisynthPluginInit3(env, InitServerFunctions());
AFAICS you save a second set of stubs, an extra step in the build process by getting rid of the lib, and everything is easier kept in sync when new functions are added because it is all in the header.
Bidoche
3rd November 2005, 18:35
and why not simply :
class VideoInfo
{
virtual bool HasVideo() const = 0;
virtual bool HasAudio() const = 0;
};
IanB
4th November 2005, 03:23
@All,
I am still hoping for a clear and clean solution. As clean as just using __declspec(dllimport) but without the static binding.
@MfA,
Thanks for some of the better naming. Nice try, but no cigar.
A. You have code in avisynth.h -- Goal -> Pure definition only!!!
B. Not keen on the manual function pointer stuffing.
C. Not sure some of your constructs will actually compile. They certainly need extreme comments as to how they are intended to work.
If I do go that way I would generate the stubs with macros from an include file, doing this sort of thing avoids transcription errors.
@Bidoche,
Don't I wish it could be that simple. I tried very hard to come up with something this elegant.
A. You cannot new (or declare) pure virtual class variables. You have to create something else that subsclasses the pure virtual class.
B. Adding the first virtual entry causes a vtable pointer to be added to the front of the class memory block moving the rest of the entries down 4 bytes. Damn!
C. Even if by some really devious method we could do this, there is a small problem of running the hidden vtable constructor code in <avisynth/server>.dll not the user_plugin.dll.
MfA
4th November 2005, 13:02
A. You have code in avisynth.h -- Goal -> Pure definition only!!!Statically linked is statically linked ... you can put it in a different file though. I think a source file is preferrable over a lib, with a lib the odds of making a mistake and getting out of sync during updates is higher ... and the only reason not to put the code in a header file is aesthetics.
B. Not keen on the manual function pointer stuffing.Not much different than manually building 2 sets of stubs. Using macros would be a good idea regardless of how you implement it.
C. Not sure some of your constructs will actually compile. They certainly need extreme comments as to how they are intended to work.Just include a link to this site (http://www.function-pointer.org/) :)
IanB
4th November 2005, 15:10
@MfA,
With code in avisynth.h, avisynth.dll won't link because of multiple redefinition of all that code, a copy of which is contributed by each module. The current way avisynth.h is declared has an implicit __inline on all that code. Sure we could frigg around with #if's but that is a rat hole we will avoid going down.
The No Code! goal help prevent misunderstanding down the track. If you allow Special Code everybody thinks their code is special and can be included. With a no code policy there is never an excuse or missunderstanding.
I am also not keen on manually building the stubs. If I were to use function pointers I would staticly declare and initialize them. I still live in (fading) hope for a really clean solution.
As to the not compiling :-
!< serverFunctions->HasVideo = &VideoInfo.HasVideo;
!< ...
probably should be
!> serverFunctions->HasVideo = &VideoInfo::HasVideo;
!> ...
If there is one thing I hate in source it is just a hyperlink for the comments for a piece of code. Because sure as God makes little green apples the link expires and you are left with no comments at all. I don't expect the entire text just a precis of the relevant idea, the hyperlink can be a bonus.
Also neat article.
As for aesthetic source, I rate this extremely highly. Code that obeys the KISS principle and looks pretty and compiles without warnings and has decent comment blocks also tends not to have bugs.
MfA
4th November 2005, 15:28
Sure we could frigg around with #if's but that is a rat hole we will avoid going down.That's what I did above. This has one big advantage, a plugin can be recompiled by just changing avisynth.h and nothing more.
From the point of view of the plugin developer this KISS. Needing to include an extra source files is less simple, and needing to include a binary lib is less simple than that.
PS. in retrospect I dont think wrapping an oo-c api in c++ is all that bad.
Bidoche
4th November 2005, 20:29
A. You cannot new (or declare) pure virtual class variables. You have to create something else that subsclasses the pure virtual class.We are talking about separating interface from implementation, isn't it just that ?
B. Adding the first virtual entry causes a vtable pointer to be added to the front of the class memory block moving the rest of the entries down 4 bytes. Damn!How is that a problem ?
Nobody should know about the entries anyway.
C. Even if by some really devious method we could do this, there is a small problem of running the hidden vtable constructor code in <avisynth/server>.dll not the user_plugin.dll.Not sure i get your point here.
If you mean we need a factory method to create an object, I am aware of that.
The solution is justclass AvisynthServer
{
virtual PVideoInfo CreateVideoInfo() const = 0;
};
MfA
4th November 2005, 20:40
How is that a problem ?
Nobody should know about the entries anyway.It's for binary compatibility of for instance videoframe objects between old and new plugins. Thinking about it, it shouldnt be that big a deal to just translate between the different implementations of the classes inside the server though.
If I were to go that far Id just create a completely new OO-C api instead with C++ wrappers for backwards source code compatibility with plugins (binary compatibility would be handled by the server). You would eliminate the compiler dependency, and you'd have a C interface for plugins which is actively maintained.
Bidoche
4th November 2005, 20:50
It's for binary compatibility of for instance videoframe objects between old and new plugins. Thinking about it, it shouldnt be that big a deal to just translate between the different implementations of the classes inside the server though.And who said binary compatibility is possible ?
Even though I named my class VideoInfo above, it's a new one (in another namespace)
If I were to go that far Id just create a completely new OO-C api instead with C++ wrappers for backwards source code compatibility with plugins (binary compatibility would be handled by the server). You would eliminate the compiler dependency, and you'd have a C interface for plugins which is actively maintained.OO-C gives up exceptions... which sucks a lot.
MfA
4th November 2005, 20:53
And who said binary compatibility is possible ?In this thread noone yet ... but if you really want I could ;)
Richard Berg
5th November 2005, 01:02
@Bidoche -- how does the plugin interface work in 3.0? I haven't kept up.
IanB
5th November 2005, 08:25
@Bidoche,
Sorry, I assumed everybody had read the main 2.6 thread goals. The aim is to support version 2.5 pluggins (with current baked code) directly and remove all the baked code from 2.6+ pluggins and have the pluggins source compatible.
If we were starting from scratch then this would be easy and most (all?) of your ideas would be right on the money.Not sure I get your point here.
If you mean we need a factory method to create an object, I am aware of that.
The solution is justclass AvisynthServer
{
virtual PVideoInfo CreateVideoInfo() const = 0;
};The problem is when building separate .dll's designing where the vtable constructor code actually comes from. You have to make sure some other code in the right .dll constructs the object and passes it back. What you have posted is only half the answer, the unspecified half is AvisynthServer objects must always be created inside avisynth.dll(or OtherServer.dll) code.
@MfA,
Patients man, patients. I am still chewing on your ideas. Something about Grandmothers, Duck eggs and sucking upon same. ;)
And to be sure you have not missed a point, If you put non-inline code in avisynth.h then every *.cpp that includes it emits that code. When you try to link you get redefinition errors. The rat hole is packing #if's around the code as a crappy fix, not gonna happen!
Bidoche
5th November 2005, 10:45
@Bidoche -- how does the plugin interface work in 3.0? I haven't kept up.What do you want to know exactly ?
So far, I am not too happy with what I came up first and it is bound to change.
MfA
5th November 2005, 15:23
What you have posted is only half the answer, the unspecified half is AvisynthServer objects must always be created inside avisynth.dll(or OtherServer.dll) code.Life would be so much easier if this was an lvalue.
IanB
9th November 2005, 15:38
Dear All,
With suggestions from MfA, here is another issue. I've gone with the function pointers this time and done away with the need for loading a library or other chunk of code, the plugin author now must call env->InitServerFunction() in their AvisynthPluginInit3 routine. On balance MfA has convinced me that it was arduous messing around with a library, so we only get 99% source compatibility.
I think I still prefer the class full of virtual functions instead of the function pointers but it makes little difference to this, it really only effect the contents of the avisynth.h generator macro, which I have not supplied.
By using a macro to emit the required baked code stubs I sort of half get my wish for no code, what code there is comes from the resolution of a macro. So at least on the surface it looks like there is no code, I can live with that.
More thoughts and comments please.//------------------------
// USER_PLUGIN.cpp side
//------------------------
...
extern "C" __declspec(dllexport) const char* __stdcall
AvisynthPluginInit3(IScriptEnvironment* env) {
// New 2.6 requirment!!!
// Initialise the server pointers
env->InitServerFunction();
// Add the name of our function
env->AddFunction("Plugin", "c", Create_Plugin, 0);
return "Plugin";
}//------------------------
// avisynth.h
//------------------------
...
// Server Function pointers
struct ServerFunctions {
...
bool (*viHasVideo)() const;
bool (*viHasAudio)() const;
...
bool (*viIsSameColorspace)(const VideoInfo &vi);
...
}
#ifndef __AVISYNTH_CORE__
// Global pointer to server function table
ServerFunctions *serverFunctions;
#define code macro plugin mode
...
#else
#define code macro server mode
...
#endif
...
struct VideoInfo {
int width, height; // width=0 means no video
...
// useful functions of the above
# In Server case a macro emits this text
=======================================
bool HasVideo() const;
bool HasAudio() const;
...
bool IsSameColorspace(const VideoInfo& vi) const;
# In Plugin case a macro emits this text
=======================================
bool HasVideo() const { return this->(serverFunctions->HasVideo)(); }
bool HasAudio() const { return this->(serverFunctions->HasAudio)(); }
...
bool IsSameColorspace(const VideoInfo& vi) const {
return this->(serverFunctions->IsSameColorspace)(const VideoInfo& vi);
}
...
};
class IScriptEnvironment {
...
// Note the default argument is the global
// pointer to the server function table
virtual void __stdcall InitServerFunction(
ServerFunctions* &vector=serverFunctions)=0;
...
};
...//------------------------
// plugins.cpp
...
// 2.5 plugin has baked code
// doesn't need protection
env->enable_AddFunction();
// Init the plugin
result = *AvisynthPluginInit2(env);
...
// 2.6 plugins have pointers to code
// Prevent env->AddFunction until
// server pointer has been loaded.
env->disable_AddFunction();
// Init the plugin
result = *AvisynthPluginInit3(env);
...//------------------------
// avisynth.cpp
void ScriptEnvironment::AddFunction(...) {
// If they haven't called InitServerFunction()
// then bark at them
if (IsAddFunctionsDisabled())
ThrowError("blah blah 2.6 Plugins must call InitServerFunction()");
function_table.AddFunction(name, params, apply, user_data);
}
...
ServerFunctions serverFunctionsVectors;
...
serverFunctions.viHasVideo = &VideoInfo::HasVideo;
serverFunctions.viHasAudio = &VideoInfo::HasAudio;
...
serverFunctions.viIsSameColorSpace = &VideoInfo::IsSameColorSpace;
...
void ScriptEnvironment::InitServerFunction(ServerFunctions* &vector)
{
// Give the plugin a pointer
// to the Server functions
vector = &serverFunctionsVectors;
// Pointer has been loaded,
// allow env->AddFunction
enable_AddFunction();
}
...//------------------------
// VideoInfo.cpp
bool VideoInfo::HasVideo() const { return (width!=0); }
bool VideoInfo::HasAudio() const { return (audio_samples_per_second!=0); }
...
bool VideoInfo::IsSameColorspace(const VideoInfo& vi) const {
if (vi.pixel_type == pixel_type) return TRUE;
if (IsYV12() && vi.IsYV12()) return TRUE;
return FALSE;
}IanB
IanB
16th November 2005, 10:20
A small refinement;
If I define the serverFunctionsVectors thus :-extern "c" __declspec(dllexport) ServerFunctions *serverFunctionsVectors;I can find and initialize it using GetProcAddress() as I load the plugin. I can then still call AvisynthPluginInit2 and leave it's contents unchanged. I also won't need InitServerFunction() or disable_AddFunction(). However I will need the plugin author to instantiate static storage of serverFunctionsVectors (well I was going to need this anyway) so source will still need a minor tweak.
Any thoughts or comments?
mg262
16th November 2005, 11:23
Ian,
I don't think you should worry about creating small amounts of work for plug-in authors. If you are going to recompile anyway, something like this is trivial -- IMO you could get away with requiring bigger changes than this, so long as they were mechanical.
Thank you for all your hard work!
sh0dan
16th November 2005, 15:53
Looks great!
MfA
16th November 2005, 20:22
However I will need the plugin author to instantiate static storage of serverFunctionsVectors (well I was going to need this anyway) so source will still need a minor tweak.
Any thoughts or comments?Id say put extra code a plugin author has to have in a .icbiinah file which you can simply include (a I Can't Believe It Is Not A Header file).
foxyshadis
17th November 2005, 03:25
Call it bakedcode.h (or .cpp) :p
IanB
17th November 2005, 12:23
Possible, but its just 1 more line of boiler plate code a plugin needs.//------------------------
// USER_PLUGIN.cpp side
//------------------------
#include "avisynth.h"
...
// New 2.6 requirment!!!
// Declare the server pointer
ServerFunctions *serverFunctionsVectors = 0;
extern "C" __declspec(dllexport) const char* __stdcall
AvisynthPluginInit2(IScriptEnvironment* env) {
// Add the name of our function
env->AddFunction("Plugin", "c", Create_Plugin, 0);
return "Plugin text";
}
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.