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";
}

mg262
17th November 2005, 12:41
IMO putting actual object instantations in header files is... ick. (Sorry,MfA!) Anyway, if it really bothers you you can create an include file with these two lines...

#include "avisynth.h"
ServerFunctions *serverFunctionsVectors = 0;

...yourself.(You can even call it AVISynth.h if you want, by playing with paths.)

IanB
31st March 2012, 01:01
Dear All,

It's been a while (a very long while), so to stop our endless spinning with endlessly trying for perfection, I have just plunged in and bashed out a solution. Wherever the concept came unglued I have just hacked in a workable kludge so we can move forward. So there is some ugliness and kludginess.

I have committed code to CVS, see here :- avisynth.h (http://avisynth2.cvs.sourceforge.net/viewvc/avisynth2/avisynth/src/core/avisynth.h?revision=1.34&view=markup), interface.cpp (http://avisynth2.cvs.sourceforge.net/viewvc/avisynth2/avisynth/src/core/interface.cpp?revision=1.9&view=markup) and plugins.cpp (http://avisynth2.cvs.sourceforge.net/viewvc/avisynth2/avisynth/src/core/plugins.cpp?revision=1.12&view=markup) Also see the end of directshow_source.cpp (http://avisynth2.cvs.sourceforge.net/viewvc/avisynth2/avisynth/src/plugins/DirectShowSource/directshow_source.cpp?revision=1.34&view=markup) for how to use the new linkage interface.

On the user plugin side the entry-point is now called AvisynthPluginInit3 and it take 2 arguments, an IScriptEnvironment* and an AVS_Linkage*. The user need to declare static storage for AVS_Linkage* AVS_linkage; and in their AvisynthPluginInit3 code save the supplied 2nd value to the AVS_linkage variable. The rest of the code should be the same as for 2.5 plugins, i.e. env->AddFunction, etc.

In avisynth.h the type AVS_Linkage struct is declared and macros emit stub baked code that calls the appropriate code in the server via the AVS_Linkage function pointers. The stub code checks to make sure AVS_linkage is not Null and the offset of the function pointer is within the current size of the structure. If okay the function is called otherwise it evaluates as 0. Within the Avisynth.dll project these macros resolve to a simple ";" so no stub code is emitted just declarations occur, this is triggered by the definition of the reserved symbol AVISYNTH_CORE.

In interface.cpp the real code is defined and the instance of the AVS_Linkage struct is declared and initialised. In plugins.cpp the address of this is passed to the users AvisynthPluginInit3 code along with the ScriptEnvironment address. A minor hurdle was that you may not take the address of constructor or destructor routines in C++ so I to move forward I just nested the appropriate code for these down one method and used the address of the nested method. I unimaginatively called these CONSTRUCTORn() and DESTRUCTOR().

In directshow_source.cpp the changes were trivial. Declare the storage for AVS_linkage, rename the .dll entrypoint from AvisynthPluginInit2 to AvisynthPluginInit3 and add the extra AVS_Linkage* argument.

Thoughts and comments please.
//------------------------
// USER_PLUGIN.cpp side
//------------------------
...
/* New 2.6 requirment!!! */
// Declare and initialise server pointers static storage.
AVS_Linkage *AVS_linkage = 0;

/* New 2.6 requirment!!! */
// DLL entry point called from LoadPlugin() to setup a user plugin.
extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(IScriptEnvironment* env, AVS_Linkage* vectors) {

/* New 2.6 requirment!!! */
// Save the server pointers.
AVS_linkage = vectors;

// Add the name of our function
env->AddFunction("Plugin", "c", Create_Plugin, 0);

// Return plugin text identifier.
return "Plugin";
}//------------------------
// avisynth.h
//------------------------
...
// Server Function pointers structure
struct AVS_Linkage {
int Size;

bool (VideoInfo::*HasVideo)() const;
bool (VideoInfo::*HasAudio)() const;
...
bool (VideoInfo::*IsSameColorspace)(const VideoInfo& vi) const;
...
int (VideoFrame::*GetPitch)(int plane) const;
...
}
...
// Macros
#ifdef AVISYNTH_CORE
/* Macro resolution for code inside Avisynth.dll */
# define AVS_BakedCode(arg) ;
# define AVS_LinkCall(arg)
#else
/* Macro resolution for code inside user plugin */
extern AVS_Linkage *AVS_linkage;

# define AVS_BakedCode(arg) { arg ; }
# define AVS_LinkCall(arg) !AVS_linkage || offsetof(AVS_Linkage, arg) >= AVS_linkage->Size ? 0 : (this->*(AVS_linkage->arg))
#endif
...
// Baked stub code
struct VideoInfo {
int width, height; // width=0 means no video
...
// useful functions of the above
bool HasVideo() const AVS_BakedCode( return AVS_LinkCall(HasVideo)() )
bool HasAudio() const AVS_BakedCode( return AVS_LinkCall(HasAudio)() )
...
bool IsColorSpace(int c_space) const AVS_BakedCode( return AVS_LinkCall(IsColorSpace)(c_space) )

};
...//------------------------
// interface.cpp
//------------------------

// Real code
bool VideoInfo::HasVideo() const { return (width!=0); }
bool VideoInfo::HasAudio() const { return (audio_samples_per_second!=0); }
...
bool VideoInfo::IsColorSpace(int c_space) const {
return IsPlanar() ? ((pixel_type & CS_PLANAR_MASK) == (c_space & CS_PLANAR_FILTER)) : ((pixel_type & c_space) == c_space);
}
...
// Static linkage structure declaration and initialisation
AVS_Linkage AVS_linkage = { // struct AVS_Linkage {

sizeof(AVS_Linkage), // int Size;

&VideoInfo::HasVideo, // bool (VideoInfo::*HasVideo)() const;
&VideoInfo::HasAudio, // bool (VideoInfo::*HasAudio)() const;
...
&VideoInfo::IsColorSpace, // bool (VideoInfo::*IsColorSpace)(int c_space) const;
...
};
...//------------------------
// plugins.cpp
//------------------------
...
typedef const char* (__stdcall *AvisynthPluginInit3Func)(IScriptEnvironment* env, AVS_Linkage* vectors);
AvisynthPluginInit3Func AvisynthPluginInit3 = (AvisynthPluginInit3Func)GetProcAddress(plugin, "AvisynthPluginInit3");
if (!AvisynthPluginInit3) {
... try other entry point names _AvisynthPluginInit3@8, AvisynthPluginInit2 & _AvisynthPluginInit2@4
} else {
result = AvisynthPluginInit3(env, &AVS_linkage);
}
...

tebasuna51
31st March 2012, 02:42
That mean than all plugins must be changed to be 2.6 compliant?

Maybe this is a good moment to add the necesary audio property mask_channels.

IanB
31st March 2012, 05:43
No 2.6 can load and use 2.5 plugins within the 2.5 feature set. That is all the current 2.6 alpha can actually do, i.e. there is no 2.6 API yet. (This thread is it)

To maintain compatibility unfortunately visible structure like VideiInfo cannot be changed.

LaTo
31st March 2012, 08:35
Great work! It may be time to put avs 2.6.0 in a beta state :)

-Vit-
2nd April 2012, 19:20
I've posted a collection of new interface plugins (http://forum.doom9.org/showpost.php?p=1568142&postcount=1126) in the QTGMC thread. No problems in early testing (against SEt's 2.6MT build). Distribution is an issue, now have two versions of each plugin. I haven't renamed the new dlls since that might cause hidden problems if the user puts both versions in the plugins folder (?). Still wondering about the best approach though.

Fizick
18th April 2012, 19:31
Thanks, IanB!

pbristow
23rd April 2012, 14:24
Great work! It may be time to put avs 2.6.0 in a beta state :)

As a user rather than a developer, I'm inclined to agree. The baked code removal/"2.6 API" is a significant change that is taking a lot of time and work to get right. Certainly worth doing, but it might be worth officially postponing it to version 2.7 (and making it the *only* change made at that version), to allow the excellent new features and performance improvements that have already gone into 2.6 (and which some of us have been using intermittently for a while now!) to become more widely established. (How practical that is, I'm not clear - i.e. is debugging/finalising the existing features dependent in some way on the baked code removal/interface changes? - but I put the suggestion here for your consideration.)

It will also do the image of AVIsynth as an active project good, I think, to have a new non-alpha version (with an offical build) finally out there!

Robert Martens
20th May 2012, 04:33
A quick, potentially dumb question, if I may: should one expect to successfully link to Avisynth 2.6 as a library yet? In preparing a version of TurnsTile (http://forum.doom9.org/showthread.php?t=158695) that works under a variety of Avisynth versions (namely Avxsynth, but that's working quite well), I've been trying to properly set up a test executable that loads Avisynth and runs through some checks. Both implicit and explicit linking work correctly with 2.5.8 (even the 64 bit version, though I won't be driving myself crazy to support it), and I've been able to compile the plugin itself as an actual new interface 2.6 plugin; AVS_linkage, PluginInit3 and all, using it in a script works as intended.

Using revision 1.34 of avisynth.h from the CVS repository, and SEt's May 16th build of 2.6MT, however, I'm getting everyone's favorite error, "unresolved external symbol "struct AVS_Linkage * AVS_linkage"" no matter how I go about linking the thing.

The loading, use and unloading of libraries is still new to me, and I intend to continue my research after posting this message, but it seemed worth checking in to see if what I want is even supposed to be possible yet. Does the API need to be finalized before this works as expected, or is my problem coming from somewhere else (e.g. me, or "post your code, you dolt")?

StainlessS
20th May 2012, 05:32
Still in the dreamy stage I suspect. Keep dreamin.

IanB
20th May 2012, 23:17
@Robert Martens,

You need to declare the storage for the AVS_linkage pointer and assign the value of the vectors argument to it in your plugin code.

See the green comments and blue code fragments in the USER_PLUGIN.cpp side section of my post above (http://forum.doom9.org/showthread.php?p=1567792#post1567792)


Thoughts on a better way to do this or to document this are very welcome.

Robert Martens
21st May 2012, 02:20
I'm sorry, I should have explained myself more clearly. The plugin compiles and operates correctly; I've set up the code as shown in your user_plugin.cpp example, AVS_linkage declared and initialized at file scope, 'vectors' passed in to AvisynthPluginInit3, and its value assigned immediately to AVS_linkage. I #ifdef between the new 2.6 interface and the existing PluginInit2 style based on a CMake-created preprocessor define, but to eliminate that as a potential complication I've tried simply hardcoding the 2.6 version, and both approaches work. The plugin is loaded without issue, and it processes video the way it's meant to.

My problem is getting the error when linking to avisynth.dll as a video processing library from my own executable. When linking to 2.5.8 implicitly (using the .lib and header) or explicitly (using LoadLibrary and the header, since the return value of GetProcAddress must be cast to a pointer to an IScriptEnvironment, a class which is only declared in avisynth.h), I can successfully create a script environment, invoke AVISource to load a clip, query its VideoInfo to get the width and height, and print that information to stdout. Trying to do the same with 2.6 gives me the unresolved external symbol error.

Do I need to do something with AVS_linkage in the executable that loads Avisynth, or is Stainless correct that I'll just have to wait for further developments?

IanB
21st May 2012, 23:20
Sorry, I misunderstood your original issue. So yes you have to wait for the next development.

Robert Martens
22nd May 2012, 01:46
Excellent, thanks for the confirmation!

IanB
1st July 2012, 01:27
Dear All,

I have had a chance to review my first efforts, resolve a few issues and add functionality for host applications to establish the AVS_Linkage.

The good news.

I have tagged the main instance of the AVS_Linkage structure static and const and I have created a Dll exported const pointer to it, AVS_linkage. This provides a static means for applications that use Avisynth.dll functions to establish the AVS_Linkage. Also I have extended IScriptEnvironment:: with GetAVSLinkage() as a dynamic method to establish the AVS_Linkage. To use the static linkage method #define AVS_LINKAGE_DLLIMPORT immediately before your #include "avisynth.h". To use the dynamic method declare AVS_linkage as in a plugin and after creating env with the CreateScriptEnvironment() do AVS_linkage = env->GetAVSLinkage().

The bad news.

I discovered that compiler was emitting dll initialisation code to build the static AVS_Linkage structure at runtime instead of allocating it as a static initialised constant structure at compile time. It was also allocating maximum size 16 byte unknown inheritance structures instead of the expected simple 4 byte units for the function pointers.

To fix the 16 byte function pointers I added __single_inheritance to the forward declarations of the appropriate classes. This also mostly fixed the stupid initialisation code emission. Seems the compiler also emits initialisation code for overloaded definitions so I added some uniquely named methods to the AVSValue class to manually resolve the problem.

This of course breaks binary compatibility so any early API adopters will need to recompile to match this version. Hopefully no other nasties appear and break future binary compatibility.

Thoughts and comments please.
//------------------------
// USER_PLUGIN.cpp side
//------------------------
...
/* New 2.6 requirement!!! */
// Declare and initialise server pointers static storage.
const AVS_Linkage *AVS_linkage = 0;

/* New 2.6 requirement!!! */
// DLL entry point called from LoadPlugin() to setup a user plugin.
extern "C" __declspec(dllexport) const char* __stdcall
AvisynthPluginInit3(IScriptEnvironment* env, const AVS_Linkage* const vectors) {

/* New 2.6 requirment!!! */
// Save the server pointers.
AVS_linkage = vectors;

// Add the name of our function
env->AddFunction("Plugin", "c", Create_Plugin, 0);

// Return plugin text identifier.
return "Plugin";
}//------------------------
// avisynth.h
//------------------------

// Forward references
struct __single_inheritance VideoInfo;
class __single_inheritance VideoFrameBuffer;
class __single_inheritance VideoFrame;
class IClip;
class __single_inheritance PClip;
class __single_inheritance PVideoFrame;
class IScriptEnvironment;
class __single_inheritance AVSValue;
...
// Macros
#ifdef AVISYNTH_CORE
/* Macro resolution for code inside Avisynth.dll */
# define AVS_BakedCode(arg) ;
# define AVS_LinkCall(arg)
#else
/* Macro resolution for code inside user plugin */
# ifdef AVS_LINKAGE_DLLIMPORT
extern __declspec(dllimport) const AVS_Linkage* const AVS_linkage;
# else
extern const AVS_Linkage* AVS_linkage;
# endif
...
// IScriptEnvironment
class IScriptEnvironment {
public:
...
virtual const AVS_Linkage* const __stdcall GetAVSLinkage() = 0;

}; // end class IScriptEnvironment...//------------------------
// interface.cpp
//------------------------

// Static linkage structure declaration and initialisation
static const AVS_Linkage avs_linkage = { // struct AVS_Linkage {

sizeof(AVS_Linkage), // int Size;
...
};
extern __declspec(dllexport) const AVS_Linkage* const AVS_linkage = &avs_linkage;
...

Robert Martens
5th July 2012, 04:54
I'm sorry to say I can't offer any terribly substantial comments, but after installing my old copy of VC6, wrestling with some configuration and SDK issues, and building the current CVS tree (I'd been using SEt's builds, but he hasn't released since these changes and I don't want to bug him) I can confirm that the linkage by an external application works. The tests I mentioned earlier now run properly when linked against 2.6, thanks for the update!

SEt
5th July 2012, 18:22
You sure don't need the ancient and buggy VC6 to build Avisynth. I'm using VS2010 for my latest builds.