Log in

View Full Version : AviSynth 2.6.0 Alpha4 [Jan 14th, 2013]


Pages : 1 2 3 [4] 5

IanB
22nd August 2013, 00:41
@Wilbert,

In a strictly authoritarian sense you could return 3 to be totally anal about it, but in practise it's not necessary. Prior to 2.6 nothing ever used GetVersion.

The key point is you need to support all the API features up to the level you return.

vcmohan
22nd August 2013, 04:20
That said, it is fairly easy to write a single .dll plugin that could be loaded and used by a 2.5 Avisynth core and still be a complete 2.6 plugin to a 2.6 Avisynth core.

To do so the .dll only needs to provide both AvisynthPluginInit2 and AvisynthPluginInit3 entry points. ----- A less brutish solution could be to provide a local static copy of the AVS_linkage for use with a 2.5 core case, hopefully the 2.6 core version of the AVS_linkage is used in the 2.6 code paths. Just needs a little imagination .....



I am not a CPP geek and also due to my age, difficult to grasp quickly. This is completely above me. Hope some thing like simple simon example will be available soon to guide.

Wilbert
22nd August 2013, 18:15
To do so the .dll only needs to provide both AvisynthPluginInit2 and AvisynthPluginInit3 entry points. One brute force solution would be to have separate 2.5 and 2.6 filter classes and reference each behind the appropriate AvisynthPluginInit* entry point.
Ok, lets start with the brutal one ;) Since others have given up :( The code below works for 2.6 and it crashes for 2.58. Yes it's only one class, but i don't see what other things i need to change to make it work for 2.5 too. I want them to use the same InvertNeg::GetFrame or isn't that possible?


#include <windows.h>
#include "avisynth.h"

class InvertNeg : public GenericVideoFilter
{
public:
InvertNeg(PClip _child, IScriptEnvironment* env);
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
};

InvertNeg::InvertNeg(PClip _child, IScriptEnvironment* env) :
GenericVideoFilter(_child) {
if (!vi.IsPlanar() || !vi.IsYUV()) {
env->ThrowError("InvertNeg: planar YUV data only!");
}
}

PVideoFrame __stdcall InvertNeg::GetFrame(int n, IScriptEnvironment* env) {

PVideoFrame src = child->GetFrame(n, env);
PVideoFrame dst = env->NewVideoFrame(vi);

const unsigned char* srcp;
unsigned char* dstp;
int src_pitch;
int dst_pitch;
int row_size;
int height;

int p, x, y;

if ((vi.IsPlanar()) && (vi.IsYUV())) {
int planes[] = {PLANAR_Y, PLANAR_V, PLANAR_U};

for (p=0; p<3; p++) {
srcp = src->GetReadPtr(planes[p]);
dstp = dst->GetWritePtr(planes[p]);

src_pitch = src->GetPitch(planes[p]);
dst_pitch = dst->GetPitch(planes[p]);
row_size = dst->GetRowSize(planes[p]);
height = dst->GetHeight(planes[p]);

for (y = 0; y < height; y++) {
for (x = 0; x < row_size; x++) {
dstp[x] = srcp[x] ^ 255;
}
srcp += src_pitch;
dstp += dst_pitch;
}
}
return dst;
} else {
return src;
}
}

AVSValue __cdecl Create_InvertNeg(AVSValue args, void* user_data, IScriptEnvironment* env) {
return new InvertNeg(args[0].AsClip(), env);
}

AVSValue __cdecl Create_InvertNeg25(AVSValue args, void* user_data, IScriptEnvironment* env) {
return new InvertNeg(args[0].AsClip(), env);
}

const AVS_Linkage *AVS_linkage = 0;

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(IScriptEnvironment* env, const AVS_Linkage* const vectors) {
AVS_linkage = vectors;
env->AddFunction("InvertNeg", "c", Create_InvertNeg, 0);
return "`InvertNeg' sample plugin";
}

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* env) {
env->AddFunction("InvertNeg", "c", Create_InvertNeg25, 0);
return "`InvertNeg' sample plugin";
}

StainlessS
22nd August 2013, 20:06
To do so the .dll only needs to provide both AvisynthPluginInit2 and AvisynthPluginInit3 entry points. One brute force solution would be to have separate 2.5 and 2.6 filter classes and reference each behind the appropriate AvisynthPluginInit* entry point. A less brutish solution could be to provide a local static copy of the AVS_linkage for use with a 2.5 core case, hopefully the 2.6 core version of the AVS_linkage is used in the 2.6 code paths. Just needs a little imagination .....




This DOES work for both v2.58 and v2.6a4 (Brute Force approach)

InvertNeg25.cpp

#include <windows.h>
#include "avisynth25.h"

class InvertNeg25 : public GenericVideoFilter
{
public:
InvertNeg25(PClip _child, IScriptEnvironment* env);
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
};

InvertNeg25::InvertNeg25(PClip _child, IScriptEnvironment* env) :
GenericVideoFilter(_child) {
if (!vi.IsPlanar() || !vi.IsYUV()) {
env->ThrowError("InvertNeg: planar YUV data only!");
}
}

PVideoFrame __stdcall InvertNeg25::GetFrame(int n, IScriptEnvironment* env) {

PVideoFrame src = child->GetFrame(n, env);
PVideoFrame dst = env->NewVideoFrame(vi);

const unsigned char* srcp;
unsigned char* dstp;
int src_pitch;
int dst_pitch;
int row_size;
int height;

int p, x, y;

if ((vi.IsPlanar()) && (vi.IsYUV())) {
int planes[] = {PLANAR_Y, PLANAR_V, PLANAR_U};

for (p=0; p<3; p++) {
srcp = src->GetReadPtr(planes[p]);
dstp = dst->GetWritePtr(planes[p]);

src_pitch = src->GetPitch(planes[p]);
dst_pitch = dst->GetPitch(planes[p]);
row_size = dst->GetRowSize(planes[p]);
height = dst->GetHeight(planes[p]);

for (y = 0; y < height; y++) {
for (x = 0; x < row_size; x++) {
dstp[x] = srcp[x] ^ 255;
}
srcp += src_pitch;
dstp += dst_pitch;
}
}
return dst;
} else {
return src;
}
}


AVSValue __cdecl Create_InvertNeg25(AVSValue args, void* user_data, IScriptEnvironment* env) {
return new InvertNeg25(args[0].AsClip(), env);
}

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* env) {
env->AddFunction("InvertNeg", "c", Create_InvertNeg25, 0);
return "`InvertNeg' sample plugin";
}


InvertNeg26.cpp

#include <windows.h>
#include "avisynth26.h"

class InvertNeg26 : public GenericVideoFilter
{
public:
InvertNeg26(PClip _child, IScriptEnvironment* env);
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
};

InvertNeg26::InvertNeg26(PClip _child, IScriptEnvironment* env) :
GenericVideoFilter(_child) {
if (!vi.IsPlanar() || !vi.IsYUV()) {
env->ThrowError("InvertNeg: planar YUV data only!");
}
}

PVideoFrame __stdcall InvertNeg26::GetFrame(int n, IScriptEnvironment* env) {

PVideoFrame src = child->GetFrame(n, env);
PVideoFrame dst = env->NewVideoFrame(vi);

const unsigned char* srcp;
unsigned char* dstp;
int src_pitch;
int dst_pitch;
int row_size;
int height;

int p, x, y;

if ((vi.IsPlanar()) && (vi.IsYUV())) {
int planes[] = {PLANAR_Y, PLANAR_V, PLANAR_U};

for (p=0; p<3; p++) {
srcp = src->GetReadPtr(planes[p]);
dstp = dst->GetWritePtr(planes[p]);

src_pitch = src->GetPitch(planes[p]);
dst_pitch = dst->GetPitch(planes[p]);
row_size = dst->GetRowSize(planes[p]);
height = dst->GetHeight(planes[p]);

for (y = 0; y < height; y++) {
for (x = 0; x < row_size; x++) {
dstp[x] = srcp[x] ^ 255;
}
srcp += src_pitch;
dstp += dst_pitch;
}
}
return dst;
} else {
return src;
}
}

AVSValue __cdecl Create_InvertNeg26(AVSValue args, void* user_data, IScriptEnvironment* env) {
return new InvertNeg26(args[0].AsClip(), env);
}


const AVS_Linkage *AVS_linkage = 0;

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(IScriptEnvironment* env, const AVS_Linkage* const vectors) {
AVS_linkage = vectors;
env->AddFunction("InvertNeg", "c", Create_InvertNeg26, 0);
return "`InvertNeg' sample plugin";
}


But, you need two of everything.

EDIT: Wilbert, v2.6 sets AVS_linkage = vectors; in v2.58 case it remains NULL and no Baked Code available from v3 header, CRASH.
v2.5, Needs something similar to whatever sets up vectors for AVS_linkage in Avisynth v2.6 source, AND a new v3 header similar to V5 redirecting via AVS_linkage.
Seems you are planning ahead for planar RGB, but repeated check in GetFrame() seems redundant.

EDIT: Above in red gotta be wrong, unless for v2.5 only (I think).

Wilbert
22nd August 2013, 20:47
But, you need two of everything.
Yes i was afraid of that.

EDIT: Wilbert, v2.6 sets AVS_linkage = vectors; in v2.58 case it remains NULL and no Baked Code available from v3 header, CRASH.
That brings us to the second method
A less brutish solution could be to provide a local static copy of the AVS_linkage for use with a 2.5 core case, hopefully the 2.6 core version of the AVS_linkage is used in the 2.6 code paths. Just needs a little imagination .....

How does this work? How do you provide a local static copy of the AVS_linkage for use with a 2.5 core case?

Seems you are planning ahead for planar RGB, but repeated check in GetFrame() seems redundant.
I'm sure there will be planar RGB one day ;) Yes i know the repeated check is redundant.

StainlessS
22nd August 2013, 20:49
See edit. Could be coded for static linkage.

IanB
22nd August 2013, 23:55
To stop having 2 of stuff you could use the namespace trick the 2.0 import wrapper uses.

Place all the filter generic code into a separate file, "InvertNeg.hpp", and #include it twice. Technically you have 2 copies of everything binary, but only a single source code. Note: outside the namespace you uniquely access object names as avs25::name or avs26::name
/* InverNeg.cpp */

namespace avs25 {

#include "avs25/avisynth.h"

#include "InvertNeg.hpp"

}

namespace avs26 {

#include "avs26/avisynth.h"

const AVS_Linkage *AVS_linkage = 0;

#include "InvertNeg.hpp"

}

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(
IScriptEnvironment* env, const avs26::AVS_Linkage* const vectors) {
avs26::AVS_linkage = vectors;
env->AddFunction("InvertNeg", "c", avs26::Create_InvertNeg, 0);
return "`InvertNeg' sample plugin";
}

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* env) {
env->AddFunction("InvertNeg", "c", avs25::Create_InvertNeg, 0);
return "`InvertNeg' sample plugin";
}

IanB
23rd August 2013, 00:46
A less brutish solution could be to provide a local static copy of the AVS_linkage for use with a 2.5 core case, hopefully the 2.6 core version of the AVS_linkage is used in the 2.6 code paths. Just needs a little imagination .....
So snaffle and modify a copy of avisynth2/avisynth/src/core/interface.cpp /* Modified Interface.cpp */
....
// 50 #include "stdafx.h"
#include <windows.h>

#define AVISYNTH_CORE // pretend we are avisynth.dll
#include "avisynth.h"

....

// 746 extern __declspec(dllexport) const AVS_Linkage* const AVS_linkage = &avs_linkage;
....


Initialise AVS_linkage to the local copy and set it to the core value in the AvisynthPluginInit3 entry code.
....
const AVS_Linkage *AVS_linkage = &avs_linkage;

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(
IScriptEnvironment* env,
const AVS_Linkage* const vectors) {

AVS_linkage = vectors;
env->AddFunction("InvertNeg", "c", Create_InvertNeg, 0);
return "`InvertNeg' sample plugin";
}

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(
IScriptEnvironment* env) {
env->AddFunction("InvertNeg", "c", Create_InvertNeg, 0);
return "`InvertNeg' sample plugin";
}

StainlessS
23rd August 2013, 01:34
....
const AVS_Linkage *AVS_linkage = &avs_linkage;

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(
IScriptEnvironment* env,
const AVS_Linkage* const vectors) {

AVS_linkage = vectors;
env->AddFunction("InvertNeg", "c", Create_InvertNeg, 0);
return "`InvertNeg' sample plugin";
}

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(
IScriptEnvironment* env) {
env->AddFunction("InvertNeg", "c", Create_InvertNeg, 0);
return "`InvertNeg' sample plugin";
}

dont quite work

interface.cpp

static const AVS_Linkage avs_linkage = { // struct AVS_Linkage {


as static.

MS Docs
When modifying a variable or function at file scope, the static keyword specifies that the variable or function has internal linkage
(its name is not visible from outside the file in which it is declared).


Errors:

InvertNeg.cpp
m:\sj\source archive\vc\avisynth filters\25\invertneg\invertneg.cpp(65) : error C2065: 'avs_linkage' : undeclared identifier
m:\sj\source archive\vc\avisynth filters\25\invertneg\invertneg.cpp(65) : error C2440: 'initializing' : cannot convert from 'int *' to 'const struct AVS_Linkage *'
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast


Added to Interface.cpp

extern __declspec(dllexport) const AVS_Linkage* const AVS_linkage_Ptr = &avs_linkage;


and to InvertNeg.cpp

extern __declspec(dllexport) const AVS_Linkage* const AVS_linkage_Ptr;

const AVS_Linkage *AVS_linkage = AVS_linkage_Ptr; // Init for For v2.5


Both Interface.cpp and InvertNeg.cpp compile OK but on linking


Compiling...
interface.cpp
InvertNeg.cpp
Linking...
InvertNeg.obj : error LNK2005: "public: bool __thiscall VideoInfo::IsYUV(void)const " (?IsYUV@VideoInfo@@QBE_NXZ) already defined in interface.obj
InvertNeg.obj : error LNK2005: "public: bool __thiscall VideoInfo::IsPlanar(void)const " (?IsPlanar@VideoInfo@@QBE_NXZ) already defined in interface.obj
InvertNeg.obj : error LNK2005: "public: int __thiscall VideoFrame::GetPitch(int)const " (?GetPitch@VideoFrame@@QBEHH@Z) already defined in interface.obj
InvertNeg.obj : error LNK2005: "public: int __thiscall VideoFrame::GetRowSize(int)const " (?GetRowSize@VideoFrame@@QBEHH@Z) already defined in interface.obj
InvertNeg.obj : error LNK2005: "public: int __thiscall VideoFrame::GetHeight(int)const " (?GetHeight@VideoFrame@@QBEHH@Z) already defined in interface.obj
InvertNeg.obj : error LNK2005: "public: unsigned char const * __thiscall VideoFrame::GetReadPtr(int)const " (?GetReadPtr@VideoFrame@@QBEPBEH@Z) already defined in interface.obj
InvertNeg.obj : error LNK2005: "public: unsigned char * __thiscall VideoFrame::GetWritePtr(int)const " (?GetWritePtr@VideoFrame@@QBEPAEH@Z) already defined in interface.obj
InvertNeg.obj : error LNK2005: "public: __thiscall PClip::PClip(class PClip const &)" (??0PClip@@QAE@ABV0@@Z) already defined in interface.obj
InvertNeg.obj : error LNK2005: "public: __thiscall PClip::~PClip(void)" (??1PClip@@QAE@XZ) already defined in interface.obj
InvertNeg.obj : error LNK2005: "public: __thiscall PVideoFrame::PVideoFrame(class PVideoFrame const &)" (??0PVideoFrame@@QAE@ABV0@@Z) already defined in interface.obj
InvertNeg.obj : error LNK2005: "public: __thiscall PVideoFrame::~PVideoFrame(void)" (??1PVideoFrame@@QAE@XZ) already defined in interface.obj
InvertNeg.obj : error LNK2005: "public: __thiscall AVSValue::AVSValue(class IClip *)" (??0AVSValue@@QAE@PAVIClip@@@Z) already defined in interface.obj
InvertNeg.obj : error LNK2005: "public: __thiscall AVSValue::~AVSValue(void)" (??1AVSValue@@QAE@XZ) already defined in interface.obj
InvertNeg.obj : error LNK2005: "public: class PClip __thiscall AVSValue::AsClip(void)const " (?AsClip@AVSValue@@QBE?AVPClip@@XZ) already defined in interface.obj
InvertNeg.obj : error LNK2005: "public: __thiscall PClip::PClip(class IClip *)" (??0PClip@@QAE@PAVIClip@@@Z) already defined in interface.obj
InvertNeg.obj : error LNK2005: "public: class AVSValue const & __thiscall AVSValue::operator[](int)const " (??AAVSValue@@QBEABV0@H@Z) already defined in interface.obj
InvertNeg.obj : warning LNK4006: "public: bool __thiscall VideoInfo::IsYUV(void)const " (?IsYUV@VideoInfo@@QBE_NXZ) already defined in interface.obj; second definition ignored
InvertNeg.obj : warning LNK4006: "public: bool __thiscall VideoInfo::IsPlanar(void)const " (?IsPlanar@VideoInfo@@QBE_NXZ) already defined in interface.obj; second definition ignored
InvertNeg.obj : warning LNK4006: "public: int __thiscall VideoFrame::GetPitch(int)const " (?GetPitch@VideoFrame@@QBEHH@Z) already defined in interface.obj; second definition ignored
InvertNeg.obj : warning LNK4006: "public: int __thiscall VideoFrame::GetRowSize(int)const " (?GetRowSize@VideoFrame@@QBEHH@Z) already defined in interface.obj; second definition ignored
InvertNeg.obj : warning LNK4006: "public: int __thiscall VideoFrame::GetHeight(int)const " (?GetHeight@VideoFrame@@QBEHH@Z) already defined in interface.obj; second definition ignored
InvertNeg.obj : warning LNK4006: "public: unsigned char const * __thiscall VideoFrame::GetReadPtr(int)const " (?GetReadPtr@VideoFrame@@QBEPBEH@Z) already defined in interface.obj; second definition ignored
InvertNeg.obj : warning LNK4006: "public: unsigned char * __thiscall VideoFrame::GetWritePtr(int)const " (?GetWritePtr@VideoFrame@@QBEPAEH@Z) already defined in interface.obj; second definition ignored
InvertNeg.obj : warning LNK4006: "public: __thiscall PClip::PClip(class PClip const &)" (??0PClip@@QAE@ABV0@@Z) already defined in interface.obj; second definition ignored
InvertNeg.obj : warning LNK4006: "public: __thiscall PClip::~PClip(void)" (??1PClip@@QAE@XZ) already defined in interface.obj; second definition ignored
InvertNeg.obj : warning LNK4006: "public: __thiscall PVideoFrame::PVideoFrame(class PVideoFrame const &)" (??0PVideoFrame@@QAE@ABV0@@Z) already defined in interface.obj; second definition ignored
InvertNeg.obj : warning LNK4006: "public: __thiscall PVideoFrame::~PVideoFrame(void)" (??1PVideoFrame@@QAE@XZ) already defined in interface.obj; second definition ignored
InvertNeg.obj : warning LNK4006: "public: __thiscall AVSValue::AVSValue(class IClip *)" (??0AVSValue@@QAE@PAVIClip@@@Z) already defined in interface.obj; second definition ignored
InvertNeg.obj : warning LNK4006: "public: __thiscall AVSValue::~AVSValue(void)" (??1AVSValue@@QAE@XZ) already defined in interface.obj; second definition ignored
InvertNeg.obj : warning LNK4006: "public: class PClip __thiscall AVSValue::AsClip(void)const " (?AsClip@AVSValue@@QBE?AVPClip@@XZ) already defined in interface.obj; second definition ignored
InvertNeg.obj : warning LNK4006: "public: __thiscall PClip::PClip(class IClip *)" (??0PClip@@QAE@PAVIClip@@@Z) already defined in interface.obj; second definition ignored
InvertNeg.obj : warning LNK4006: "public: class AVSValue const & __thiscall AVSValue::operator[](int)const " (??AAVSValue@@QBEABV0@H@Z) already defined in interface.obj; second definition ignored
Creating library Debug/InvertNeg.lib and object Debug/InvertNeg.exp
Debug/InvertNeg.dll : fatal error LNK1169: one or more multiply defined symbols found
Error executing link.exe.

InvertNeg.dll - 17 error(s), 16 warning(s)

Robert Martens
23rd August 2013, 02:30
Stainless, I think you just need to forward declare avs_linkage to get rid of the "undeclared identifier" message:

...
static const AVS_Linkage avs_linkage;
const AVS_Linkage* AVS_linkage = &avs_linkage;

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(
IScriptEnvironment* env, const AVS_Linkage* const vectors)
...

And then take care of the duplicate symbol errors by making sure every inclusion of avisynth.h in your project's files is done with #define AVISYNTH_CORE before it. I hate to dive into the discussion having only just tried this, but that cleared up the build errors for me, and it seems like it works after building and testing in both 2.6 and 2.5.8. I'm open to being smacked upside the head if I'm doing this wrong.

StainlessS
23rd August 2013, 02:42
Tried as in my previous post with additional #define AVISYNTH_CORE,

and with

static const AVS_Linkage avs_linkage;
const AVS_Linkage* AVS_linkage = &avs_linkage;

in InvertNeg (removing the ptr),

both compile and link OK (thought the static forward decalre would fail, but did not) but
provided an access violation error in v2.5.

You are such a tease. :)

EDIT: works after building and testing in both 2.6 and 2.5.8, maybe I did something wrong.

EDIT: Would this not create a second avs_linkage, static in InvertNeg module (whereas the initialized one is static
in Interface module) rather than combining into a single static AVS_Linkage.


static const AVS_Linkage avs_linkage;
const AVS_Linkage *AVS_linkage = &avs_linkage;

Robert Martens
23rd August 2013, 03:12
Would this not create a second avs_linkage, static in InvertNeg module (whereas the initialized one is static
in Interface module) rather than combining into a single static AVS_Linkage.


static const AVS_Linkage avs_linkage;
const AVS_Linkage *AVS_linkage = &avs_linkage;


Yes, I'd think it would; I made the mistake of using "static" when I'd meant "extern", but correcting my code I see that introduces a new set of errors.

Using the approach I rather irresponsibly suggested, though, still seems to work for my own plugin, if only I could decipher why. I can't successfully use InvertNeg, though, so obviously something's wrong with my idea.

I should probably learn C++ one of these days.

StainlessS
23rd August 2013, 03:17
I should probably learn C++ one of these days.

Me too.

Strange but if I try to debug, it does not crash (although output video is weird, with some kind of temporal chroma lead/lag).

EDIT:
Wilbert,
int planes[] = {PLANAR_Y, PLANAR_V, PLANAR_U};

PLANAR_U(1) and PLANAR_V(2) transposed (should not cause problems though).

EDIT: and ++x; produces better code than x++;although not much in it.

EDIT: The above x++; ++x;
The first one 'x++;' implies for a stack variable, that the variable x is gotten off stack , incremented and then updated
on-stack afterwards. The 2nd '++x;' can increment variable directly on-stack (if possible in assembler instruction set),
rather than doing it post increment.

Robert Martens
23rd August 2013, 06:24
I think I've managed to work this out; my plugin, TurnsTile, was working because I use my own computed tile width and height, and the input videoinfo's width and height members, but never call GetRowSize or GetHeight. It seems that in the 2.58 code those follow the old fashioned approach of assuming U/V pitch and height are equal to half the Y plane pitch and height, respectively. The 2.6 code for both of those functions relies upon the VideoFrame members row_sizeUV and heightUV, which go uninitialized in 2.58 since they don't actually exist in that version. They end up with insane values, which then affect InvertNeg's loop count, leading to access violations.

I came to the conclusion by first following Ian's instructions as he provided them, then doing what I mentioned earlier and adding #define AVISYNTH_CORE before all includes of the header. Next, in the copy of interface.cpp that I swiped from Avisynth, I removed the static and const keywords from AVS_Linkage; apparently each of those implies, among other things, internal linkage, so the object in question won't be visible outside that source file. If this is a bad idea, please do let me know, I suppose in that case we can put the keywords back and just combine interface.cpp with our own code into one source file.

Over in my other source file, where I define the plugininit functions, I did the same as I did a few posts up but with the proper keyword this time:

...
extern AVS_Linkage avs_linkage;
const AVS_Linkage* AVS_linkage = &avs_linkage;

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(
IScriptEnvironment* env, const AVS_Linkage* const vectors) {
...

This lets the project build properly, but it still doesn't work, so I just threw a little something into the logic of the filter for testing purposes:

...
row_size = dst->GetRowSize(planes[p]);
height = dst->GetHeight(planes[p]);
if (p == 1 || p == 2) {
row_size = child->GetVideoInfo().width / 2;
height = child->GetVideoInfo().height / 2;
}

for (y = 0; y < height; y++) {
for (x = 0; x < row_size; x++) {
...

The plugin then loads (in 2.58; comment out the blue section for 2.60), and YV12 video is processed as expected. As for the proper way to modify the local copy of interface.cpp to handle this, assuming there is one, I'm still working my way through that.

TurboPascal7
23rd August 2013, 07:54
I'm not sure if someone asked this already, but is there any reason why we can't remove unaligned crop or at least make it optional? I believe it's the only function that might produce unaligned results in 2.6 and I really don't see any reasons to keep it this way. Am I missing something?

Also, any plans on making frames 32-bytes aligned for AVX?

ultim
23rd August 2013, 13:00
I'm hacking around in the AviSynth code, currently mainly familiarizing myself with it, but with ambitious goals. I've already made some progress, and I will probably open a thread for my efforts once I feel confident enough, but now I just have a general question. Given that the "standard/official" AviSynth is single-threaded by nature, why is it littered full with atomic increments and critical sections? Is there some aspect I missed that these are needed?

vcmohan
23rd August 2013, 14:40
The brief was 2.6 would load and support 2.5 plugins, period!

For me I do not want to meddle with Avisynth core. The alternatives suggested possibly produce in one package two plugins one for each version. It will double the size.
I will stick to the mandate and have a cpp file as suggested by stainless in his earlier post.

IanB
24th August 2013, 00:40
I did sayJust needs a little imagination .....so the hacking of Interface.cpp was obviously going to need a little thought and effort apart from the obvious #include's at the top and the extern __declspec at the bottom. All the old 2.5 code is still there in the comments.

In terms of size most plugin code is tiny. Things like fancy tables and runtime libraries usually make up most of the .dll size and these would occur only once. So I would not expect most dual mode plugins to be very much bigger.


@TurboPascal7,

Aligned crop costs an unaligned bitblt, i.e. unaligned read with aligned write in a loop.

For a plugin that does not have an unaligned path and crashes on unaligned reads, then you have no choice.

But for code with a usable unaligned path you are comparing [unaligned read + aligned write + aligned read] with [unaligned read] clearly the second case is faster.

So why do some filters suffer so badly with the unaligned case. Maybe they revert from a fast SSE path to a slow C++ code path, yes that's a bit slack, but maybe an unaligned SSE path won't go fast. More insidious is needing to read the input multiple times then the comparing becomes [1 *unaligned read + 1 * aligned write + N * aligned read] with [N *unaligned read], here the first case pulls ahead as N increases.

As for 32 byte alignment it may become the default in the future, but you have always been able to ask for extra alignment on the env->NewVideoFrame call.

TurboPascal7
24th August 2013, 02:13
@IanB,

The problem is added complexity. By having unaligned crop in the core, you essentially destroy all alignment guarantees said core might provide, asking all developers to add multiple code paths to handle unaligned case, thus making implementation harder (and helping introducing template/macro hell), always using slow unaligned loads or simply break with (https://github.com/chikuzen/TCannyMod/blob/master/avisynth/src/tcannymod.cpp#L139) or without error message. Is marginal performance difference in one filter in some cases worth it? You might make unaligned crop optional so people who think it matters for them could still shoot in their feet, but I don't think it's reasonable.

Strict alignment (16 or even better - 32) guarantee would IMHO simplify a lot of things for developers, especially when plugin is using the same codebase for avisynth and vapoursynth versions (vsynth guarantees 32-bytes alignment). It might be even better to completely ignore alignment parameter in env->NewVideoFrame or make 32 the minimum accepted value, but I'm not sure how many (broken) filters that might break.

Groucho2004
24th August 2013, 02:41
@IanB

I have a question about AVS_Linkage. In the following code, my program crashes (exactly here: "~AVSValue() AVS_BakedCode( AVS_LinkCall(AVSValue_DESTRUCTOR)() )") if the script (test.avs) does not return a clip. However, if I uncomment the line "AVS_linkage = 0" (bold and red in the code) it seems to work OK. Just wondering if setting "AVS_linkage" to 0 is correct in this context.

Or maybe I should just use "ThrowError()" and let Avisynth clean up by itself.


#include "avisynth.h"

const AVS_Linkage *AVS_linkage = 0;

int main()
{
HINSTANCE hDLL = ::LoadLibrary("avisynth");
if (!hDLL)
return -1;

try
{
IScriptEnvironment *(__stdcall *CreateEnvironment)(int) = (IScriptEnvironment *(__stdcall *)(int))::GetProcAddress(hDLL, "CreateScriptEnvironment");
if (!CreateEnvironment)
{
::FreeLibrary(hDLL);
return -1;
}

IScriptEnvironment *AVS_env = CreateEnvironment(AVISYNTH_INTERFACE_VERSION);
if (!AVS_env)
{
::FreeLibrary(hDLL);
return -1;
}

AVS_linkage = AVS_env->GetAVSLinkage();
AVSValue AVSV_main;
AVSV_main = AVS_env->Invoke("Import", "test.avs");

if (!AVSV_main.IsClip()) //not a clip
{
//AVS_linkage = 0;
AVSV_main = 0;
AVS_env->DeleteScriptEnvironment();
::FreeLibrary(hDLL);
return -1;
}
}
catch(AvisynthError err)
{
}

::FreeLibrary(hDLL);

return 0;
}

IanB
25th August 2013, 00:09
If we look under the covers of the baked macros :-# define AVS_BakedCode(arg) { arg ; }
# define AVS_LinkCall(arg) !AVS_linkage || offsetof(AVS_Linkage, arg) >= AVS_linkage->Size ? 0 : (this->*(AVS_linkage->arg))
We see :-
~AVSValue() AVS_BakedCode( AVS_LinkCall(AVSValue_DESTRUCTOR)() )
which becomes after we unwrap AVS_BakedCode :-
~AVSValue() { AVS_LinkCall(AVSValue_DESTRUCTOR)() ; }
and finally becomes :-
~AVSValue() {
!AVS_linkage
||
offsetof(AVS_Linkage, AVSValue_DESTRUCTOR)()) >= AVS_linkage->Size
?
0
:
(this->*(AVS_linkage->AVSValue_DESTRUCTOR)()));
}
and if we follow the function pointer :-
void AVSValue::DESTRUCTOR() { if (IsClip() && clip) clip->Release(); }
So I find it a little strange the crash report is on the link call itself rather than in the actual code, but that may be a lack of symbols thing with the debugger.

Of course setting AVS_linkage = 0 cause the code to be skipped, so what ever is wrong is ignored.

I suspect your problem may be earlier with AVSV_main not being constructed correctly :-

AVS_linkage = AVS_env->GetAVSLinkage();
AVSValue AVSV_main;
AVSV_main = AVS_env->Invoke("Import", "test.avs");

it depends on the order the compiler does things, but I suspect AVSV_main tries to get made with AVSValue::CONSTRUCTOR0() before AVS_linkage gets assigned.

So setting AVS_linkage back to zero before the ::FreeLibrary is the right thing to do, but setting it to zero before all the classes are finished is not right. Just like trying to start classes is not right before you initially set AVS_linkage.

I think we need some notes about lifetime and scope of things needing a valid AVS_linkage.

Groucho2004
25th August 2013, 00:30
So setting AVS_linkage back to zero before the ::FreeLibrary is the right thing to do, but setting it to zero before all the classes are finished is not right.
Thanks Ian. I now tested these 2 options:
if (!AVSV_main.IsClip()) //not a clip
{
AVSV_main = 0;
AVS_env->DeleteScriptEnvironment();
AVS_linkage = 0;
::FreeLibrary(hDLL);
return -1;
}

if (!AVSV_main.IsClip()) //not a clip
AVS_env->ThrowError("Script did not return a video clip");

Both let the program (a function actually) exit without crashing. I guess I'll use the former.

Edit: Static linking with avisynth.lib also works fine.

vcmohan
28th August 2013, 14:10
The windows 32 bit dll is dated jan 2013. Since then a number of changes/ corrections were done. Will it be possible to update the dll and make available for down load?

StainlessS
28th August 2013, 20:16
vcmohan,
See Grouch02004 ICL compiler version v2.6a4 with updates, current April 25 2013, works great. :)
You just need install v2.6a4 and overwrite dll in system32 (SysWOW64 64bit) with the new dll.

http://forum.doom9.org/showthread.php?t=167358

Robert Martens
29th August 2013, 01:03
Ian, I wonder if you'd be so kind as to offer a little more help regarding your idea for the dual-host plugin as described here:

Initialise AVS_linkage to the local copy and set it to the core value in the AvisynthPluginInit3 entry code.
....
const AVS_Linkage *AVS_linkage = &avs_linkage;

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(
IScriptEnvironment* env,
const AVS_Linkage* const vectors) {

AVS_linkage = vectors;
env->AddFunction("InvertNeg", "c", Create_InvertNeg, 0);
return "`InvertNeg' sample plugin";
}

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(
IScriptEnvironment* env) {
env->AddFunction("InvertNeg", "c", Create_InvertNeg, 0);
return "`InvertNeg' sample plugin";
}

I've spent the past few days going over C++ books, websites, Stack Overflow posts, basically all the reference material I could get my hands on, digging further into function pointers than I ever thought I would, along with inheritance and a variety of other related topics, but I feel like I'm going around in circles.

Including the Avisynth header with AVISYNTH_CORE defined means the linkage macros evaluate to nothing, so all the header contains are function declarations. I'm then free to implement them as I see fit in interface.cpp. That's great for 2.5, but loading the plugin in 2.6 shows that it uses those same local implementations, since the various methods aren't defined to call AVS_linkage and get the appropriate function pointers from the core.

Trying to include the header twice (once in interface.cpp with AVISYNTH_CORE defined, and once in invertneg.cpp without it) just ends up defining the functions twice, which won't work, and I spun my wheels in the mud playing around with derived classes, friend functions, and function pointer casts only to find myself back where I started.

The only answer I could come up with involves initializing AVS_linkage to zero, and then making every function in my copy of interface.cpp look like this (after copying some of the macro code into the file so it'll evaluate to code calling the function pointer):


bool VideoInfo::HasVideo() const
{

if (AVS_linkage) {
return AVS_LinkCall(HasVideo)();
} else {
return (width!=0);
}

}

As seen above, however, you suggested initializing AVS_linkage differently, so I'm fairly sure this isn't what you had in mind. Would you by any chance be able to give me a hint where I should go next? I don't expect you to teach me remedial C++, or walk me through the intricacies of the Avisynth codebase, but a nudge in the right direction would be a big help; maybe just the names of the C++ or general programming concepts I'd need to study to solve the problem the way you intended?

StainlessS
29th August 2013, 02:12
Mucho admiration that you seem to be pursuing this, thought it might better be in the domain of a CPP progger, (me given up),
Hope Mr B gives you an audience.

IanB
29th August 2013, 06:31
A solution to the functions defining twice is to wrap one instance in a namespace, then they become distinct. This is pretty much how the first brute solution gets around the same problem of duplicate names.

I did warn that some imagination would be required.
namespace Interface {
/* Modified Interface.cpp */
....
// 50 #include "stdafx.h"
#include <windows.h>

#define AVISYNTH_CORE // pretend we are avisynth.dll
#include "avisynth.h"

....

// Old 2.5 version of code
int VideoFrame::GetHeight(int plane) const {
switch (plane) {
case PLANAR_U:
case PLANAR_V:
if (pitchUV)
return height>>1;
return 0;
}
return height;
}
/* int VideoFrame::GetHeight(int plane) const {
switch (plane) {
case PLANAR_U:
case PLANAR_V:
if (pitchUV)
return heightUV;
return 0;
}
return height;
}*/

....

// 746 extern __declspec(dllexport) const AVS_Linkage* const AVS_linkage = &avs_linkage;
....
}
And of course you need to reference the namespace version of thing in Interface.cpp
...
const AVS_Linkage* AVS_linkage = &Interface::avs_linkage;
...

ultim
29th August 2013, 15:40
In the definition of the ScriptEnvironment::NewPlanarVideoFrame method, what is the point of letting plugins force a smaller alignment than the default? Anything else than those very few bytes of memory savings?

Chikuzen
29th August 2013, 22:44
From core/parser/script.cpp

AVSValue Import(AVSValue args, void*, IScriptEnvironment* env)
{
...
TCHAR full_path[MAX_PATH];
TCHAR* file_part;
if (strchr(script_name, '\\') || strchr(script_name, '/')) {
DWORD len = GetFullPathName(script_name, MAX_PATH, full_path, &file_part);
if (len == 0 || len > MAX_PATH)
env->ThrowError("Import: unable to open \"%s\" (path invalid?)", script_name);
} else {
DWORD len = SearchPath(NULL, script_name, NULL, MAX_PATH, full_path, &file_part);
if (len == 0 || len > MAX_PATH)
env->ThrowError("Import: unable to locate \"%s\" (try specifying a path)", script_name);
}
...

Although Windows API can handle MAX_PATH(260) characters(not bytes) as filepath, 'Import' causes an error by about 130 non-ASCII characters by shortage of a buffer.
(Generally, non-ASCII characters require 2bytes on ACP.)
I think that 'TCHAR full_path[MAX_PATH]' should be changed into 'TCHAR full_path[MAX_PATH * 2]'.

IanB
29th August 2013, 22:58
@ultim,

Forcing alignment is generally for setting a PVideoFrame to describe an existing foreign video buffer layout. This can avoid an unnecessary copy of the data. e.g. a source filter to match the memory layout delivered by a codec, or matching the memory layout of a hardware device.

Setting some pointers and a few describing numbers is of order a million times faster than actually copying the data bytes. TurboPascal7 above was advocating always making Crop that million times slower for the sake of always maintaining memory alignment.

IanB
29th August 2013, 23:21
@Chikuzen,

Not sure how you this is coming unstuck here, dumb ansi code page encoding was unimaginatively designed with 1 char is 1 byte. When (If) we move to UTF-8 then some code points can be up to 4? bytes so we will certainly need to cope with it then. Do you have a specific ansi code page in mind that exceeds this crippled design, one of the Japanese sets maybe?

I can certainly bump the buffer size, I just want to be able to make sure some internal library buff[MAX_PATH] is still not going to crash and burn.

Chikuzen
29th August 2013, 23:54
Do you have a specific ansi code page in mind that exceeds this crippled design, one of the Japanese sets maybe?
yes, I consider about CP932 (http://en.wikipedia.org/wiki/Code_page_932) .
I don't know which code page is used in China, Korea, Vietnam,etc...it will be the same since tens of thousands of characters are used in East Asia.
it will be better to make it MAX_PATH * 4, of course.
Stack overflow does not happen only 1KB array.

ultim
30th August 2013, 13:07
@ultim,

Forcing alignment is generally for setting a PVideoFrame to describe an existing foreign video buffer layout. This can avoid an unnecessary copy of the data. e.g. a source filter to match the memory layout delivered by a codec, or matching the memory layout of a hardware device.

Setting some pointers and a few describing numbers is of order a million times faster than actually copying the data bytes. TurboPascal7 above was advocating always making Crop that million times slower for the sake of always maintaining memory alignment.

Thanks for your help. I understand your reasoning, but the situation you described does not fully apply here, as far as I can tell. Because NewPlanarVideoFrame/NewVideoFrame always request a VideoFrame with an internal VideoFrameBuffer as a backing store (either a new one or a cached one, but always an internal one). So the existing foreign buffer that needs to be mimicked must still be copied over anyway. And in that case we might as well request a buffer with the highest alignment, so that future filters can employ special instruction sets.

Also, another thing that for me seems to invalidate your argument, is that to completely mimic a foreign buffer, you also need to mimic its pitch. NewPlanarVideoFrame/NewVideoFrame do not take pitch as an argument, so by method declaration it is impossible to use these methods to always describe an external buffer correctly. Internally these methods use rowsize and align to "guess" the pitch, but that won't always work and you're just relying on luck.

Here is an example where guessing the pitch as now won't work:
- foreign buffer rowsize is mod8/mod4/mod2/odd
- foreign rowsize = foreign pitch
- requesting alignment on 16 byte boundary
Here, because rowsize=pitch, rows after the 1st will not be 16-byte aligned, but it still makes sense to accelerate processing (e.g with SSE) if neighboring pixels are independent of each other (e.g. brightness/exposure adjustment, or simple recoloring techniques). Yet, the current methods will calculate a pitch that is mod16, so you can't describe the foreign buffer at all.

So, to summarize my points,
- because the method declarations are inadaquate to describe foreign video correctly in every case
- and because these methods will always use an internal backing store for the frame anyway, thus a copy must always be made ...
... even after your previous explanation, I still think that in these methods it doesn't make sense to force small alignments (using a negative alignment value). It only hinders later processing filters.

I hope I didn't embarass myself by writing something stupid in this long post :)

IanB
30th August 2013, 23:42
@ultim, It's not a perfect system but it serves what has been required so far.

Clarification :- The start of first line of the video frame is always 16 byte aligned! The "align" value only determines how the "pitch" is calculated from the "rowsize"....
const int pitch = (row_size+align-1) / align * align;
...
const int offset = (-int(vfb->GetWritePtr())) & (FRAME_ALIGN-1); // align first line offset
...

ultim
31st August 2013, 23:12
...
const int pitch = (row_size+align-1) / align * align;
...
const int offset = (-int(vfb->GetWritePtr())) & (FRAME_ALIGN-1); // align first line offset
...

Yes I know, that is what I meant when I said 'these methods use rowsize and align to "guess" the pitch'. I made changes to avisynth already, and I am also trying to correct this shortcoming of "align usage". Unfortunately I had to realize at some point that it is impossible to correct this in a sane way without breaking ABI compatibility. Currently, it seems at the very least I'd need to change the constructor parameters to VideoFrameBuffer, which broke at least the ffms2 plugin :( So I am still thinking about how to improve this aspect without too much trouble.

I forked avisynth locally and I'm making various "improvements". I have a variety of changes ranging from bugfixes and performance improvements, to improving developer friendliness, adding new functionality, and interface extensions in a backwards-compatible way (using COM rules ofc). I'll push my code to GitHub at some later point. I'm afraid you won't like many of them, coz I also restructured the project, moved files and code around and cleaned up header files. These add a LOT of noise to the commits, but thanks to these I feel the code is much less scary for newcomers (so I hope more contributors will show up), and I also reduced compile time by more than 50% (half!).

Anyway, thanks for your help, and I'm pretty sure I'll come back for more :)

IanB
31st August 2013, 23:49
@ultim,

There is no "guess". It is the Avisynth API definition of pitch.

What is there to improve? The API does not mandate alignment, it's historical. Recent caveats do strongly suggest that performance critical memory addresses should be appropriately aligned such that fast code paths are possible, but code must work correctly (but it is allowed to be slow) with what is provided. It is not acceptable to chuck an exception or other wobbly at runtime just because something is not aligned as you would like. It is however acceptable to throw the exception at script load time.


If you have bugs, Report them!

ultim
1st September 2013, 00:33
There is no "guess". It is the Avisynth API definition of pitch.

Well, to understand my problem, you have to keep in perspective my original issue: what the use of forced alignment is. You told me it is to mimic external buffers. Fair enough, except that now you define pitch using alignment, and external code (except for avisynth filters) will not. So you shouldn't be using alignment as pitch, OR you shouldn't be able to force it.

What is there to improve?
Exactly what I just described a line before.

If you have bugs, Report them!
From the top of my head, I have three things on my fixed-list as of now.
- Messed up SEH+C++ exception interoperation, which borkes under many compilers. Fixed.
- A frame number clamping issue in ComparePlane::CmpPlaneSame, which requires already faulty input parameters to actually manifest, so no big deal, very minor. And actually, because I have not taken the time to properly understand that function, I am only 99% sure it is a bug to begin with, but reasonably sure. Fixed.
- Two BitBlt errors. This is the "funniest" one of all, one correctness error, and another one which prevents it from manifesting but causes performance degradation, both fixed. Unfortunately I have only recently found out that some external plugins (TIVTC at least) already rely on the buggy behavior (darn!), so this cannot be fixed anyway without breaking some plugins. Not good. Which means I will probably revert this change and add the corrected version in a new interface, but I'm still thinking about the best way to handle such cases. Fixing the plugins themself is actually also an option, coz some changes that I'm planning for the future are gonna make that necessary anyway.

See, except maybe for CmpPlaneSame, your are either not affected, or no-fix-possible. So pls don't go all red on me. Also, as you see, I fix anything that I find, so reporting bugs becomes an extra (and tbh somewhat needless) hassle for both you and me when I'm gonna publish the fix anyway. I'm not gonna keep them to myself, don't worry, I'm also working on the code for others, just like you.

Robert Martens
1st September 2013, 06:14
I did warn that some imagination would be required.

I completely understand the value of "exercises left to the reader", as they say, and I'd even go so far as to say I enjoy them. It feels good to figure things out for oneself. At a certain point, however, my imagination gets tapped out, and the options are either ask for help or give up and walk away. I felt the former would be more productive, and possibly more helpful to others.

Thank you for the namespace advice, I'd toyed with that briefly but only around a few functions, not the entire file. I'm not having much luck with it so far, but I'll keep at it and see if I can get anywhere.

In the meantime, I felt I should share what my imagination has gotten me so far, so I put up a Gist with my changes: https://gist.github.com/ItEndsWithTens/6402453 The changes are summarized at the top of interface.cpp. I didn't include a project, just to keep this lightweight, but you should be able to just add the three files to your own and build it without too much hassle.

I can't promise it's the best solution (adding those branches to each function will no doubt have some performance impact), but I'm tentatively confident that it's at least working, so if anyone would like to take a look, or correct my thinking, feel free. If I ever manage a cleaner approach, I'll commit it to the Gist and make a note here.

IanB
2nd September 2013, 09:17
@Robert Martens,

I think your main difficulty was not grasping there needs to be 2 distinct API instances. One AVS_Linkage based standard 2.6 API in the InvertNeg.cpp filter and a second 2.5 API stand in from the modified interface.cpp, this second API stand in of course needs to be in a separate namespace so this implementation does not conflict with the first AVS_Linkage based implementation.

Robert Martens
2nd September 2013, 14:50
I think your main difficulty was not grasping there needs to be 2 distinct API instances.

That actually did get through to me, I understood what you meant when you offered the broad overview of "create a static copy of the linkage and initialize with that"; I didn't understand how to do that, though. That namespaces were the solution didn't occur to me, and once you mentioned that I tried it only to run into problems (which I'll get to presently). The code I posted as a Github gist is merely my desperate, last ditch effort to find a solution that offered one copy of the source code, and one copy of everything binary. Since I couldn't figure out how to do what you'd suggested, I wanted to at least find some way to make it work.

I was confused primarily because I was never sure what exactly was expected of me. You don't owe me your time, and I don't like to say these things for fear of seeming a belligerent ingrate, but I had trouble understanding your posts when you first suggested this, with regard to InvertNeg.cpp:

....
const AVS_Linkage *AVS_linkage = &avs_linkage;

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(
IScriptEnvironment* env,
const AVS_Linkage* const vectors) {

AVS_linkage = vectors;
env->AddFunction("InvertNeg", "c", Create_InvertNeg, 0);
return "`InvertNeg' sample plugin";
}

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(
IScriptEnvironment* env) {
env->AddFunction("InvertNeg", "c", Create_InvertNeg, 0);
return "`InvertNeg' sample plugin";
}

Then went on to mention the namespace solution, clarifying the initialization to this:

...
const AVS_Linkage* AVS_linkage = &Interface::avs_linkage;
...

Before coming to the end result which in your sample project turns out to be:

const AVS_Linkage* AVS_linkage = 0;

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(
IScriptEnvironment* env, const AVS_Linkage* const vectors) {
AVS_linkage = vectors;
env->AddFunction("InvertNeg", "c", Create_InvertNeg, 0);
return "`InvertNeg' sample plugin";
}

namespace AVSInterface25 {

const AVS_Linkage* AVS_linkage;

}

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* env) {
AVS_linkage = AVSInterface25::AVS_linkage;
env->AddFunction("InvertNeg", "c", Create_InvertNeg, 0);
return "`InvertNeg' sample plugin";
}

I've always operated under the assumption that when you, or anyone else on these boards, describe things in only broad strokes, we're expected to put our minds to it and discover the answer ourselves, but when you offer specific lines of code, they are to be treated as sacrosanct and left untouched. We may have to add to them to complete the solution, but we're not meant to meddle in what specific details were already spelled out. I only reached the solution I posted earlier by hesitantly changing your directions, in a flailing, grasping-at-straws desire to get anything that worked; I certainly wasn't comfortable abandoning your instructions. In the future I'll try to be more flexible in my problem solving.

Unfortunately, although VC6 works as expected, I'm having trouble with your sample in my usual environment of Visual Studio 2010 Express; building the project spits out a series of errors related to sourceannotations.h, the first of which is

sourceannotations.h(244): error C3083: 'vc_attributes': the symbol to the left of a '::' must be a type

Including Windows.h inside the namespace seems to be the issue here. Moving the include out of it solves that problem, but introduces a new one:

crtdbg.h(1064): error C2039: 'delete[]' : is not a member of '`global namespace''

Since the crtdbg header is only included inside avisynth.h, I'm left believing the only option would be modifying the Avisynth header, but I'm not entirely sure what I should do, or that I even should be doing something so intrusive. I could always pull the crtdbg/debug macro definition block out of the header and add it by hand to all my source files, but that seems ugly. Is it my only option?

IanB
2nd September 2013, 23:04
Okay more than fair enough, you got the concept, just your SDK is putting up a fight about how to make it work.

I find it very hard to describe this sort of stuff without resorting to "loose" code examples, generally code snippets I use for discussion should be treated as definitely untested and probably requiring some sort of context.

Looks like I underestimated the amount of sugar that was going to be needed here. As you note I gave up on this actual construct :-const AVS_Linkage* AVS_linkage = &Interface::avs_linkage;I tried to make that idea fly for a full 5 minutes and replaced it with this :-namespace AVSInterface25 {
const AVS_Linkage* AVS_linkage;
}
.....
AVS_linkage = AVSInterface25::AVS_linkage;
.....and yes I got that "is not a member of '`global namespace''" old flannel from the compiler along the way. You just have to keep punching away until you find a syntax that represents the idea and will actually compile.

Not sure about the sourceannotations.h(244), maybe some "LEAN_AND_MEAN" declarations can help.

Pier
3rd September 2013, 11:25
I was wondering if it was possible to add an ,int "offset" to ConditionalReader in the next version to replace the offset value in the txt file, i mean, for example, if have written varius .txt files with varius kind of data to make something like IsSectionStatic() i would need to write a very large ammount of .txt files for every frame with a different offset value, but if could just use an int "offest", a recursion would allow me to check all the frames until the next scenechange and some other nice thing.

Wilbert
3rd September 2013, 22:08
I thought let's try the second brute force method again (with InvertNeg.hpp as you posted; and changed red parts):


/* InvertNeg.cpp */

#include <windows.h>

namespace avs25 {
#include "avisynth25.h"
#include "InvertNeg.hpp"
}

namespace avs26 {
#include "avisynth26.h"
const AVS_Linkage *AVS_linkage = 0;
#include "InvertNeg.hpp"
}

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(avs25::IScriptEnvironment* env) {
env->AddFunction("InvertNeg", "c", avs25::Create_InvertNeg, 0);
return "InvertNeg sample plugin";
}

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(avs26::IScriptEnvironment* env, const avs26::AVS_Linkage* const vectors) {
avs26::AVS_linkage = vectors;
env->AddFunction("InvertNeg", "c", avs26::Create_InvertNeg, 0);
return "InvertNeg sample plugin";
}


If I only keep the avs25 sections (including the namespace) it works. If I only keep the avs26 sections (including the namespace) it works. But keeping both, just as above, when compiling i get the errors:

Compiling...
InvertNeg.cpp
F:\CompilingPlugins\InvertNeg\InvertNeg.cpp(11) : error C2143: syntax error : missing ';' before '*'
F:\CompilingPlugins\InvertNeg\InvertNeg.cpp(11) : error C2734: 'AVS_Linkage' : const object must be initialized if not extern
F:\CompilingPlugins\InvertNeg\InvertNeg.cpp(11) : error C2501: 'AVS_linkage' : missing storage-class or type specifiers
F:\CompilingPlugins\InvertNeg\InvertNeg.hpp(2) : error C2504: 'GenericVideoFilter' : base class undefined
F:\CompilingPlugins\InvertNeg\InvertNeg.hpp(4) : error C2629: unexpected 'class avs26::InvertNeg ('

etc ... No idea why it doesn't work. Btw line 11, is the const AVS_Linkage *AVS_linkage = 0; line.

Robert Martens
5th September 2013, 00:44
Wilbert, I think that's the include guard you're bumping up against. Both versions of the Avisynth header define the same name, and you're including them both in the same file. If you just undefine it between your namespace blocks you should avoid the problem:

namespace avs25 {

#include "avs25/avisynth.h"

#include "InvertNeg.hpp"

}

#undef __AVISYNTH_H__

namespace avs26 {

#include "avs26/avisynth.h"

const AVS_Linkage *AVS_linkage = 0;

#include "InvertNeg.hpp"

}


On the topic of the method Ian and I had been discussing, the problem seems to be that sourceannotations, when __cplusplus is defined, creates a namespace called "vc_attributes", then ends up using the scope resolution operator to assume it's directly under the global namespace, i.e. ::vc_attributes. Wrapping the include of that file in our own namespace means the full name ends up being ::AVSInterface25::vc_attributes, and the code in that header can't find anything. The same goes for crtdbg.h.

A little further digging with the /showIncludes switch (in VS2010 it's under Properties->C/C++->Advanced->Show Includes) shows that the aforementioned problem header is being pulled in by way of windows.h->excpt.h->crtdefs.h->sal.h->codeanalysis\sourceannotations.h.

The file's inclusion in sal.h is wrapped with this:

#if defined(_MSC_EXTENSIONS) && !defined( MIDL_PASS ) && !defined(__midl) && !defined(RC_INVOKED)

My brief research leads me to think those are of no use, though I suppose if push came to shove one could always use RC_INVOKED to pretend the resource compiler is active. Of course, even if you manage to avoid including the file, what happens to the rare bird who wants to use source code annotation? Academic, maybe, but the thought crossed my mind.

In any event, I finally got around to toying with VS 2012 Express, and it has no problems with the code, even with both includes under a custom namespace. Personally I don't mind switching, despite the disappointing Metro UI, but I'm still curious to see if I can come up with a solution for 2010. I'll keep at it.

Wilbert
6th September 2013, 22:06
Wilbert, I think that's the include guard you're bumping up against. Both versions of the Avisynth header define the same name, and you're including them both in the same file. If you just undefine it between your namespace blocks you should avoid the problem:
Yes that works fine. Thanks!

Robert Martens
7th September 2013, 20:12
Ian, I'm sorry to say that although I got your version of the project to build successfully, I realized I'd never actually tried to use it in 2.5.8. I gave it a shot a short while ago, only to see everyone's favorite "System Exception: Access Violation" message. Stepping through the code with the debugger showed that the assignment in AvisynthPluginInit2 wasn't actually taking effect, for some reason that still escapes me.

There is nonetheless what I've convinced myself is excellent news! Find at the Github Gist (https://gist.github.com/ItEndsWithTens/6402453) a new version of the code, more in keeping with your original idea. Again, the changes to interface.cpp from revision 1.13 of that file are summarized at the top, but to explain what I did in relation to our discussion so far:

I was off base regarding VS 2012 before; the inclusion of sourceannotations.h appears to occur under different criteria than in 2010, but the file is still there, and may still cause problems for anyone using that functionality. crtdbg.h seems more robust, in that although operator delete and operator delete[] are declared in the file, they are no longer defined there as in 2010.

That's only in Visual Studio 2012, of course, so to account for both of those issues in all compiler/SDK versions, in interface.cpp I kept the inclusion of Windows.h out of the namespace (thereby addressing sourceannotations) and pilfered the debug macro definition from avisynth.h. Contrary to my earlier muddled expectation, this only needs to be done in interface.cpp, since it's the only place we're wrapping the header in a namespace. Placing the include of crtdbg and assert.h outside the namespace has the added benefit that if you ever use anything else in that file that expects to find the assert features, it'll work properly (I ran into that myself with avxsynth (https://github.com/avxsynth/avxsynth/issues/99)). I took the liberty of commenting out the _RPTX macros to avoid warnings about their redefinition, since I know the header will always be there to define them if _MSC_VER is undefined.

I then made avs_linkage non-static and non-const, so it's visible outside interface.cpp.

Over in invertneg.cpp, I made everything below Create_InvertNeg look like this:

namespace AVSInterface25 {

struct AVS_Linkage;
extern AVS_Linkage avs_linkage;

}

const AVS_Linkage* AVS_linkage = reinterpret_cast<AVS_Linkage*>(&AVSInterface25::avs_linkage);

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(
IScriptEnvironment* env, const AVS_Linkage* const vectors) {
AVS_linkage = vectors;
env->AddFunction("InvertNeg", "c", Create_InvertNeg, 0);
return "`InvertNeg' sample plugin";
}

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* env) {
env->AddFunction("InvertNeg", "c", Create_InvertNeg, 0);
return "`InvertNeg' sample plugin";
}

I found the cast necessary to allow the initialization. reinterpret_cast is ugly, but it expresses what we're doing more clearly than a C-style cast, and although it's a dangerous cast to develop the habit of using, in this case we know the two structures are identical, differing only by one's existence in a namespace.

How does this approach strike everyone?

vcmohan
9th September 2013, 02:14
When the input video is not planar format, GetWidthSubsampling() and GetHeightSubsampling() give an error message while GetReadPtr or GetPitch and other calls do not. Why this special treatment?

IanB
9th September 2013, 03:27
New code enforces correctness, old code a little bit slack.

Also GetPlaneWidth/HeightSubsampling are members of VideoInfo and do not have a useful value to return on error. (0 is no subsampling, -1 --> times 2 super sampling, 1 --> times 2 subsampling)

And GetReadPtr, GetPitch are members of VideoFrame and can usefully return zero for non existent planes.

StainlessS
9th September 2013, 17:30
@VcMohan

As you recently posed a query on compiling for both v2.58 and v2.6a4, I assume that you want subsampling for both.
Below some code I use in RoboCrop, perhaps of use:

myName="RoboCrop: ";

if(vi.width==0 || vi.num_frames==0) env->ThrowError("%sClip has no video",myName);
# ifdef AVISYNTH_PLUGIN_25
if(vi.IsPlanar() && vi.pixel_type != 0xA0000008) { // Planar but NOT YV12, ie Avisynth v2.6+ ColorSpace
// Here Planar but NOT YV12, If v2.5 Plugin Does NOT support ANY v2.6+ ColorSpaces
env->ThrowError("%sColorSpace unsupported in v2.5 plugin",myName);
}
# endif

bool isyuv=false,isplanar=false,isyuy2=false,isrgb=false,isy8=false;
int xmod=1,ymod=1;

if(isyuv=vi.IsYUV()) {
if(isplanar=vi.IsPlanar()) {
PVideoFrame src = child->GetFrame(0, env); // get frame 0
int rowsizeUV=src->GetRowSize(PLANAR_U);
if(rowsizeUV!=0) { // Not Y8
const int ywid=src->GetRowSize(PLANAR_Y);
const int yhit=src->GetHeight(PLANAR_Y);
const int uhit=src->GetHeight(PLANAR_U);
xmod=ywid/rowsizeUV;
ymod=yhit/uhit;
} else {
isy8=true;
}
} else {
isyuy2=true;
xmod=2;
}
} else {
isrgb=true;
}

xSubS=xmod; // for Planar GetFrame
ySubS=ymod;

laced = args[3].AsBool(true);
ymod = (laced)? ymod*2 : ymod;


v2.58 header does not contain GetWidthSubsampling/GetHeightSubsampling :helpful:

martin53
9th September 2013, 21:46
Please forgive me if I'm wrong or if the issue is already known - I couldn't find a similar post.

I suspect a 'memory leak' in the ConvertToYV24() function of V2.60.
I load the test script
ColorBars()
#~ ConvertToYV24()
ScriptClip("return last")
with XMediaRecode and observe its memory usage with ProcessExplorer. While converting the script to a HuffYuv AVI file, XMediaRecode's memory usage stays constant btw. 87 and 90 MBytes.
With the uncommented ConvertToYV24() Line, memory usage grows and grows. With more complex scripts (a ~15 line scriptclip that overlays 4 lines calculated with RTE stats functions over a Histogram("color2") :( ), the encoder crashes when memory usage reaches about 1,8MBytes.
VDub Portable (1.9.11) also crashes with a message "Think we ran out of memory folks".
OS details in this thread (http://forum.doom9.org/showthread.php?t=168538)

EDIT: This is the crash script. Omit 'ConvertToYV24()', then it uses about 560MB, not more.
#=====================================================================================================================
function UVMeterBad(clip c) {
c
ScriptClip(""" #"
c = Last
c1 = c.crop(0,0,-256,0)
cU = c1.UToY.BicubicResize(c1.width, c1.height)
cV = c1.VToY.BicubicResize(c1.width, c1.height)
MYStats(cU, flgs=$23)
Um= MYS_yMin #cU.RT_YPlaneMin()
Ux= MYS_yMax #cU.RT_YPlaneMax()
Us= round(MYS_yStdev) #round(cU.RT_YPlaneStdev)
MYStats(cV, flgs=$23)
Vm= MYS_yMin #cV.RT_YPlaneMin()
Vx= MYS_yMax #cV.RT_YPlaneMax()
Vs= round(MYS_yStdev) #round(cV.RT_YPlaneStdev)
Ymax = c1.YPlaneMax()
MYStats(cU, c1, flgs=$10, prefix="MUS_", MaskMin=Ymax-1)
MYStats(cV, c1, flgs=$10, prefix="MVS_", MaskMin=Ymax-1)
c
Overlay(BlankClip(c,pixel_type="RGB32",color=$80ff00,width=57,height=1),x=c.width-157,y=127,mode="Exclusion") #cross hor.
Overlay(BlankClip(c,pixel_type="RGB32",color=$80ff00,width=1,height=57),x=c.width-129,y=99,mode="Exclusion") #cross vert.
Overlay(BlankClip(c,pixel_type="RGB32",color=$ffffff,width=Ux-Um,height=1),x=c.width-129+Um-128,y=127+round(MVS_yAve)-128,mode="Exclusion") #range hor.
Overlay(BlankClip(c,pixel_type="RGB32",color=$ffffff,width=1,height=Vx-Vm),x=c.width-129+round(MUS_yAve)-128,y=127+Vm-128,mode="Exclusion") #range vert.
return last
""", local=true) #"
return last
}
#=====================================================================================================================
ColorBars(pixel_type="YV12")
ConvertToYV24()
Histogram("color2")
UVmeterBad()