Log in

View Full Version : Invoke separatefields


Ceppo
30th January 2021, 08:08
I was writing a plugin then I just realized that the code could be much shorter if I could just call separate fields on a clip. I tried and the code should be the following:

AVSValue args[1] = { child };
PClip separatefields = env->Invoke("SeparateFields", AVSValue(args, 1)).AsClip();

It compiles but it doesn't work, which means that I'm making some kind of semantic error in my code. Rather than sharing my messy code I would like if someone could fill the template that I will give you below with the following instructions so that I can understand what I'm doing wrong.
1) Declare the separatefields clip in the constructor.
2) Access it in getframe.
3) Return it so that I get the very same output as separatefields().

#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)
{

}

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

return separatefields;
}

AVSValue __cdecl Create_InvertNeg(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";
}

wonkey_monkey
30th January 2021, 13:08
GetFrame should return a PVideoFrame, not a PClip. I'd be surprised if it would compile like that, so it would be more helpful if you posted your actual code, otherwise we're not going to see where you're going wrong - otherwise you might just make the same mistake when rebuilding your plugin from any template someone provides, and be none the wiser.

StainlessS
30th January 2021, 14:09
I think you really need to do the SeparateFields, in Create_InvertNeg(), and pass result to new InvertNeg().
Separate-ing fields on the clip, and process the result in your filter.
Maybe you also need to weave() result of InvertNeg(), before return. EDIT: Also in Create_InvertNeg().

EDIT:
Maybe something like [untested]

AVSValue __cdecl Create_InvertNeg(AVSValue args, void* user_data, IScriptEnvironment* env) {
PClip child = args[0].AsClip();
AVSValue separgs[1] = { child };
PClip sep = env->Invoke("SeparateFields", AVSValue(separgs, 1)).AsClip();
PClip neg = new InvertNeg(sep, env);
AVSValue weaveargs[1] = { neg };
AVSValue result = env->Invoke("Weave", AVSValue(weaveargs, 1)).AsClip();
return result;
}

Ceppo
30th January 2021, 15:13
AVSValue separgs[1] = child;
...
AVSValue weaveargs[1] = neg;

The following lines give the error "initialization with {...} expected for aggregate object" so it doesn't compile.

However a SeparateFields().InvertNeg() approach won't work for me since I need GetParity to tell me if the video is top or bottom field first. Basically I need a Separatefield clip to do my math and then rebuild a frame from the fields outputting a bobbed clip. So I need the steps I asked to figure out why for example the following code doesn't work. I'm new to C++ so it might make no sense but whatever.

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

try {
AVSValue args[1] = { child };
PClip sep = env->Invoke("SeparateFields", AVSValue(args, 1)).AsClip();
}
catch (IScriptEnvironment::NotFound) {
env->ThrowError("MyFilterError: Whoa! Could not Invoke separatefields!");
}

PVideoFrame sep_c = sep->GetFrame(n, env);
PVideoFrame sep_p = sep->GetFrame(n == 0 ? n : n - 1, env);
PVideoFrame sep_n = sep->GetFrame(n == vi.num_frames - 1 ? n : n + 1, env);

return sep_c;
}

StainlessS
30th January 2021, 15:30
OK, I cocked up removing the curly braces, just use replace them.
You can use GetParity() in GetFrame.
From old v2.58 header.

virtual bool __stdcall GetParity(int n) = 0; // return field parity if field_based, else parity of first field in frame


EDIT: If you do Separatefields in GetFrame, there will be some [small] overhead in doing that.

StainlessS
30th January 2021, 15:44
Colorbars

Assumetff
#AssumeBff

#SeparateFields

SSS="""
n = current_frame
Parity = GetParity(n)
Subtitle(String(n,"%.0f] ")+String(Parity))
"""
ScriptClip(SSS)

Return last

Ceppo
30th January 2021, 16:28
GetFrame should return a PVideoFrame, not a PClip. I'd be surprised if it would compile like that, so it would be more helpful if you posted your actual code, otherwise we're not going to see where you're going wrong - otherwise you might just make the same mistake when rebuilding your plugin from any template someone provides, and be none the wiser.

From the examples here (http://avisynth.nl/index.php/Filter_SDK/Env_Invoke) is PClip. About the code is badly written since it was a test draft so I would have to rewrite it all from scratch anyway.


virtual bool __stdcall GetParity(int n) = 0; // return field parity if field_based, else parity of first field in frame

Good to know I will make some tests.


Colorbars

Assumetff
#AssumeBff

#SeparateFields

SSS="""
n = current_frame
Parity = GetParity(n)
Subtitle(String(n,"%.0f] ")+String(Parity))
"""
ScriptClip(SSS)

Return last


What did you want to show me with this?

May I ask for some help?

videoh
30th January 2021, 16:33
May I ask for some help?

I would start by asking for clarification on your initial statement:

I was writing a plugin then I just realized that the code could be much shorter if I could just call separate fields on a clip. What is your plugin doing and why do you think the code could be much shorter by invoking SeparateFields? It is easy for a filter to process by fields without code bloat.

Ceppo
30th January 2021, 16:58
What is your plugin doing and why do you think the code could be much shorter by invoking SeparateFields? It is easy for a filter to process by fields without code bloat.
Basically, I'm attempting to make a bob filter for telecined sources. To do that I take the current field and I make the difference with the previous and next field. Based on the result I build a frame from the fields. This results in a c/p match for the first field and a c/n match for the second field. Then I process leftover combed and fields without a match but this is another matter.

EDIT: The problem with the frame approach is the following:
I call

vi.num_frames *= 2;
vi.SetFPS(vi.fps_numerator * 2, vi.fps_denominator);

In the constructor because I want a bobbed clip, then I call

n = n/2

in get frame to return the correct frame. I get a duplicate so I throught to track the original n value to show one field when n is even and the other field when n is odd but it continues to show only the first field, so to get around this I throught to use separatefields.

videoh
30th January 2021, 17:15
I call

vi.num_frames *= 2;
vi.SetFPS(vi.fps_numerator * 2, vi.fps_denominator);

In the constructor because I want a bobbed clip, then I call

n = n/2

OK, so far so good. The n /= 2 is done in GetFrame(), so it will have access to the original n and n/2.

I get a duplicate so I thought to track the original n value to show one field when n is even and the other field when n is odd but it continues to show only the first field, so to get around this I thought to use separatefields. In GetFrame() you just test (n & 1), where n is the n coming in to GetFrame() before dividing by 2. If it is true you have an odd field. If it is false you have an even field. Based on that you process the even or odd lines of the frame. Have a look at the source code for some bob-based deinterlacers, such as the old DGBob or others. I'd assume that the Avisynth internal Bob() would be similar, but I haven't looked at it.

I usually do something like this to keep track of both n and n/2 (pseudocode)

GetFrame(frame, ...)
{
int n = frame / 2;
...
if (frame & 1)
// odd field
else
// even field
}

Compare to the source code here:

http://rationalqm.us/dgbob/dgbob.html

Ceppo
30th January 2021, 18:07
Seems to work, I don't have that problem anymore :D but I'm still curious as to why this code compiles but doesn't load

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

try {
AVSValue args[1] = { child };
PClip sep = env->Invoke("SeparateFields", AVSValue(args, 1)).AsClip();
}
catch (IScriptEnvironment::NotFound) {
env->ThrowError("MyFilterError: Whoa! Could not Invoke separatefields!");
}

PVideoFrame sep_c = sep->GetFrame(n, env);
PVideoFrame sep_p = sep->GetFrame(n == 0 ? n : n - 1, env);
PVideoFrame sep_n = sep->GetFrame(n == vi.num_frames - 1 ? n : n + 1, env);

return sep_c;
}

wonkey_monkey
30th January 2021, 19:36
Seems to work, I don't have that problem anymore :D but I'm still curious as to why this code compiles but doesn't load


By putting "PClip sep" in the "try" block you're declaring a new variable called sep that only exists within that block. By the time you get to to the three GetFrames, that sep no longer exists.

Since you say it compiles, I can only assume that you have also declared a PClip called sep as a class variable, and that's the variable you meant to refer to. Take out the "PClip" in the "try" block and it might start working the way you expect it to.

StainlessS
30th January 2021, 20:36
What did you want to show me with this?


Was just intended that you could play with it to show parity when with/without SeparateFields, if you needed to.

Ceppo
31st January 2021, 17:31
By putting "PClip sep" in the "try" block you're declaring a new variable called sep that only exists within that block. By the time you get to to the three GetFrames, that sep no longer exists.

Since you say it compiles, I can only assume that you have also declared a PClip called sep as a class variable, and that's the variable you meant to refer to. Take out the "PClip" in the "try" block and it might start working the way you expect it to.
Even without the try block it compiles but doesn't load in avspmod. Also I didn't declare it as a class variable.
Here the whole code:

#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)
{
vi.num_frames *= 2;
vi.SetFPS(vi.fps_numerator * 2, vi.fps_denominator);
}

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

AVSValue args[1] = { child };
PClip sep = env->Invoke("SeparateFields", AVSValue(args, 1)).AsClip();

PVideoFrame sep_c = sep->GetFrame(n, env);
PVideoFrame sep_p = sep->GetFrame(n == 0 ? n : n - 1, env);
PVideoFrame sep_n = sep->GetFrame(n == vi.num_frames - 1 ? n : n + 1, env);

return sep_c;
}


AVSValue __cdecl Create_InvertNeg(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";
}

videoh
31st January 2021, 18:25
First, make sure you have the right bit-ness for everything, e.g., build your filter as 64 bit, load only 64-bit filters in your script, and use 64-bit AvsPmod.

Second, add this line in your constructor:

vi.height /= 2; # because you get half height from SeparateFields()

When I do these things your filter runs just fine in VirtualDub2 and AvsPmod 64 with this script:

loadplugin("...\dgdecodenv.dll")
loadplugin("...\InvertNeg.dll")
dgsource("VTS_02_1.dgi")
InvertNeg()

If you still have a problem, please give the exact error message you encounter.

Be aware that you will incur a performance penalty doing things this way versus accepting a full frame and processing only the lines you need.

Ceppo
31st January 2021, 22:10
It works now! Thanks and thanks to everyone for the help :D