Log in

View Full Version : Help modifing plugin source


jmac698
9th November 2011, 21:38
Hi all,
Many have suggested to me that the things I'm trying to accomplish in script would be easier as a plugin. I agree. What remains for me to be productive is only that I can learn to make a plugin. I've tried to do so, the result is my article,
http://avisynthnew.wikinet.org/wiki/Avisynth_Plugin_Development_in_C#Passing_Parameters_to_your_Plugin

I was not able to complete the article, as the steps included will not work. A lot of the errors came up when I changed the file extension to .C, which seemed to indicate to my IDE to engage pure C mode, which then lead to a number of errors. I was able to fix some of these.

Edit: the code below compiles, but I doubt it's correct. Suggestions?

// Merge plugin, written by jmac698, modified from Demosaic plugin
//
// Originally written by Written by Balazs OROSZI
// Copyleft 2007.11.
//
// You may use this plugin however you wish,
// and distribute it under the terms of the GNU GPL
//
// Plugin parameters:
// ------------------
// Merge(clip clip1, clip clip2, int weight)
//
// clip clip: input clip, can be YUY2, YV12 or Y8
// weight to merge clip1*weight+(1-weight)*clip2
//
// output: YUV color image

#include "avisynth_c.h"
#include <stdlib.h>
#include <string.h>

// ---------- avisynth_c interface extension - begin ----------
// The following is missing from avisynth_c interface version 0.20 and is needed by this plugin.
// It can safely be removed, as soon as it is implemented.
enum {
AVS_CS_Y8 = 1<<8 | AVS_CS_YUV | AVS_CS_PLANAR, // Y 4:0:0 planar
};

AVSC_INLINE int avs_is_y8(const AVS_VideoInfo * p)
{ return (p->pixel_type & AVS_CS_Y8) == AVS_CS_Y8; }
// ---------- avisynth_c interface extension - end ----------

typedef struct Parms
{
AVS_Clip* clip1;
AVS_Clip* clip2;
int weight;
} Parms;

AVS_VideoFrame* AVSC_CC get_frame(AVS_FilterInfo* fi, int n)
{
// parameters
// don't know what the old line was for

// destination
AVS_VideoFrame* dest_frame;
BYTE* dest_data;
unsigned int dest_pitch, dest_height;
dest_frame = avs_new_video_frame(fi->env, &fi->vi);
dest_data = avs_get_write_ptr(dest_frame);
dest_pitch = avs_get_pitch(dest_frame);
dest_height = avs_get_height(dest_frame);

// source
AVS_VideoFrame* src_frame = avs_get_frame(fi->child, n);
const BYTE* src_data;
unsigned int src_pitch;
unsigned int src_row_size; // in bytes!
unsigned int src_height;
unsigned int src_inc; // next pixel in row

// YV12
if (avs_is_yv12(avs_get_video_info(fi->child))) { // Important: fi->child!
src_data = avs_get_read_ptr_p(src_frame, AVS_PLANAR_Y);
src_pitch = avs_get_pitch_p(src_frame, AVS_PLANAR_Y);
src_row_size = avs_get_row_size_p(src_frame, AVS_PLANAR_Y); // in bytes!
src_height = avs_get_height_p(src_frame, AVS_PLANAR_Y);
src_inc = 1;
}
// YUY2
else if (avs_is_yuy2(avs_get_video_info(fi->child))) {
src_data = avs_get_read_ptr(src_frame);
src_pitch = avs_get_pitch(src_frame);
src_row_size = avs_get_row_size(src_frame); // in bytes!
src_height = avs_get_height(src_frame);
src_inc = 2;
}
// Y8
else if (avs_is_y8(avs_get_video_info(fi->child))) {
src_data = avs_get_read_ptr(src_frame);
src_pitch = avs_get_pitch(src_frame);
src_row_size = avs_get_row_size(src_frame); // in bytes!
src_height = avs_get_height(src_frame);
src_inc = 1;
}
// somethings very wrong
else {
avs_release_frame(src_frame); // only when writing to new clip
return dest_frame;
}

// RGB is upside down
dest_data += (dest_height - 1) * dest_pitch;

src_data += 2 * src_pitch;
unsigned int y;
unsigned int sx;
unsigned int dx;
for (y = 2; y < src_height; y += 2) {
for (sx = 2 * src_inc, dx = 0; sx < src_row_size; sx += 2 * src_inc, dx += 2 * 4) {
// c<row><col>
const unsigned int c11 = (src_data - 2 * src_pitch)[sx - 2 * src_inc];
const unsigned int c12 = (src_data - 2 * src_pitch)[sx - 1 * src_inc];
const unsigned int c13 = (src_data - 2 * src_pitch)[sx - 0 * src_inc];
const unsigned int c21 = (src_data - 1 * src_pitch)[sx - 2 * src_inc];
const unsigned int c22 = (src_data - 1 * src_pitch)[sx - 1 * src_inc];
const unsigned int c23 = (src_data - 1 * src_pitch)[sx - 0 * src_inc];
const unsigned int c31 = (src_data - 0 * src_pitch)[sx - 2 * src_inc];
const unsigned int c32 = (src_data - 0 * src_pitch)[sx - 1 * src_inc];
const unsigned int c33 = (src_data - 0 * src_pitch)[sx - 0 * src_inc];

dest_data[dx + 0] = c21; // B
dest_data[dx + 1] = (c22 + c11) / 2; // G
dest_data[dx + 2] = c12; // R

dest_data[dx + 4 + 0] = c23; // B
dest_data[dx + 4 + 1] = (c22 + c13) / 2; // G
dest_data[dx + 4 + 2] = c12; // R

(dest_data - dest_pitch)[dx + 0] = c21; // B
(dest_data - dest_pitch)[dx + 1] = (c22 + c31) / 2; // G
(dest_data - dest_pitch)[dx + 2] = c32; // R

(dest_data - dest_pitch)[dx + 4 + 0] = c23; // B
(dest_data - dest_pitch)[dx + 4 + 1] = (c22 + c33) / 2; // G
(dest_data - dest_pitch)[dx + 4 + 2] = c32; // R
}
src_data += 2 * src_pitch;
dest_data -= 2 * dest_pitch;
}

avs_release_frame(src_frame); // only when writing to new clip
return dest_frame;
}

void AVSC_CC free_filter(AVS_FilterInfo* fi)
{
Parms *params = (Parms *)malloc(sizeof(Parms));
free(params);
}

AVS_Value AVSC_CC create_filter(AVS_ScriptEnvironment* env, AVS_Value args, void* param)
{
AVS_Value retval;
AVS_FilterInfo* fi;
AVS_Clip* new_clip = avs_new_c_filter(env, &fi, avs_array_elt(args, 0), 1);

// Instance data
//Demosaic* params = new Demosaic();
Parms *params = (Parms *)malloc(sizeof(Parms));

// Parameters
//Parms params;

int pnum = 0;
++pnum; params->clip2 = avs_defined(avs_array_elt(args, pnum)) ? avs_take_clip(avs_array_elt(args, pnum), env) : 0;
++pnum; params->weight = avs_defined(avs_array_elt(args, pnum)) ?
avs_as_int(avs_array_elt(args, pnum)) : 1; // Default value for weight here

// Check params
retval = avs_void;
if (!avs_defined(retval) && !avs_is_yuy2(&fi->vi)) {
retval = avs_new_value_error("Input video format can be: YUY2");
}
if (!avs_defined(retval) && !avs_is_int(avs_array_elt(args, 2))) {
retval = avs_new_value_error("weight must be an int");
}
// If no errors, all is fine, return clip value
if (!avs_defined(retval)) {
retval = avs_new_value_clip(new_clip);
}

// Set up FilterInfo
fi->user_data = (void*) params;
fi->get_frame = get_frame;

// Set up VideoInfo
fi->vi.pixel_type = AVS_CS_BGR32;

avs_release_clip(new_clip);
return retval;
}

const char* AVSC_CC avisynth_c_plugin_init(AVS_ScriptEnvironment* env)
{
avs_add_function(env, "Merge", "cc[weight]i", create_filter, 0);
return "Merge plugin";
}

Gavino
9th November 2011, 22:09
Here's just one issue;

//Demosaic* params = new Demosaic();

This failed to compile any longer. It seems to be C++ code thus I have no idea what it does.
I'm sure if you hadn't got frustrated and given up, you could have figured this out for yourself - it's just allocating menory for an instance of Demosaic. In pure C, you would use
Demosaic *params = (Demosaic *)malloc(sizeof(Demosaic));
(error handling left for you to deal with.)

The corresponding deallocation will need to be done with free() instead of the C++ 'delete'.

You also need to change constructs like
enum Mosaic {
BAYER
};

struct Demosaic {
Mosaic mosaic;
};
into the earlier C-style:
typedef enum Mosaic {
BAYER
} Mosaic;

typedef struct Demosaic {
Mosaic mosaic;
} Demosaic;

I know you don't want to hear this, but if you want my advice, I would forget the C interface, which is designed for masochists, and use C++ instead (with a free Visual C++ compiler). The amount of actual C++ you would have to learn is minimal as the basic plugin structure can be copied from examples. In return, the API is much simpler to learn and use.

jmac698
9th November 2011, 22:19
That's fine, if someone wants to write a guide. I had asked StainlessS about this some time ago, I believe he started something but didn't finish it.

jmac698
9th November 2011, 23:29
I updated and it compiles now. Any other thoughts?
And before I dig into C++, if I can at least get this working when it's so close, I think I'd know enough to write any plugin!

jmac698
10th November 2011, 03:42
So I have a simple question
I was trying something like

params->weight=avs_array_elt(args, pnum).d.integer;

But I can use this thing I found in avisynth_c.h instead:

params->weight=avs_as_int(avs_array_elt(args, pnum));

or even yours:

params->weight=as_int(avs_array_elt(args, pnum));

What I'm wondering is, why do I have to be sure that argument pnum is an avs_int when I defined it with
avs_add_function(env, "corrline", "cci"
doesn't that mean the argument #2 (3rd argument) is always going to be an int?
And what happens when I use something optional like [weight]i, if it's avs_defined does that mean I can assume it's an avs_is_int?
Finally what about more parameters that could be in different order like [weight1]i[colorspace]s do I have to check and sort them out myself, from arg#2 onwards?

Gavino
10th November 2011, 10:39
I was trying something like

params->weight=avs_array_elt(args, pnum).d.integer;

But I can use this thing I found in avisynth_c.h instead:

params->weight=avs_as_int(avs_array_elt(args, pnum));

You should never access the internal structure (eg 'd') directly - as it says in avisynth_c.h, "To maintain source code compatibility with future versions of the avisynth_c API don't use the AVS_Value directly. Use the helper functions below." (ie avs_as_int, etc)

What I'm wondering is, why do I have to be sure that argument pnum is an avs_int when I defined it with
avs_add_function(env, "corrline", "cci"
doesn't that mean the argument #2 (3rd argument) is always going to be an int?
I covered this in my earlier comments - you can safely assume that the data types are correct, the parser checks the call to your plugin matches the parameters definition (eg "cci"). What your code may need to do is check, for example, that a clip has the right properties, or an integer is in a valid range.

And what happens when I use something optional like [weight]i, if it's avs_defined does that mean I can assume it's an avs_is_int?
Yes.

Finally what about more parameters that could be in different order like [weight1]i[colorspace]s do I have to check and sort them out myself, from arg#2 onwards?
No, heaven forbid! The parser does all that work and gives you the arguments in the order defined in the AddFunction string.

jmac698
10th November 2011, 14:51
Oh that's awesome! That makes it much easier. Thanks.

So it seems the open source examples I've been learning from were not good examples to learn from. That sure doesn't help learning.

StainlessS
10th November 2011, 20:23
That's fine, if someone wants to write a guide. I had asked StainlessS about this some time ago, I believe he started something but didn't finish it.

Hi JMac

Thought you had given up on the C++ thing, so I did not bother
going much further. Saw your quoted comment and have added some to it yesterday.
Will probably need attention of a CPP'er to correct the undoubted myriad of errors and
someone with a better grasp of English to fix my screwups and speeling.

Anyways, find what I hope can be one day useful, if you can persuade another
to fix the mistakes. I dont want to maintain it,
if someone wants to put it somewhere, Wiki or whereever, so
it can be user maintained.

See in sig MediaFire and look for :-

CPP-for_C-Proggers-Example_v0.1_Source.zip

EDIT:- Or preferably, see this thread here:-

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

jmac698
10th November 2011, 20:31
Thanks,
I'll try to finish my article and can use your example too. My unfinished article is at http://avisynthnew.wikinet.org/wiki/Avisynth_Plugin_Development_in_C#Passing_Parameters_to_your_Plugin

Also I manage to make a plugin, I just released something actually useful too.

StainlessS
10th November 2011, 20:38
Also I manage to make a plugin, I just released something actually useful too.

Great, on the unfinished article, getting a 404 on the Demosaic Source link.

Not Found

The requested URL /warpenterprises/...c_20071206.zip was not found on this server.

Additionally, a 404 Not Found error was encountered while trying to use an ErrorDocument to handle the request.

jmac698
10th November 2011, 20:44
The link in the Passing Parameters section has been fixed. The first link was fine.

StainlessS
11th November 2011, 08:07
Hi Jmac, I've done a conversion to CPP of the Demosaic plug, and it produces what looks
like identical output to the C version. Dont know what it's actually supposed to do
(the active GetFrame() code was pretty much copy & paste, & I never examined what
it does).
Also, did a conversion of your Merge plug (renamed MergeCPP for comparisons).
I was not sure where you are going with it so I just did a straight average 50/50
blend, but you can get the idea and see what the equivalent ++ version
might look like.

/*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

// MergeCPP plugin, MergeCPP(clip,clip, int "weight")
//
// input clips, can be YUY2, optional weight is int, default 1

#include <windows.h>
#include <math.h>
#include "Avisynth.h"

// class MergeCPP
class MergeCPP : public GenericVideoFilter {
private:
PClip child2;
int weight;
public:
MergeCPP(PClip _child,PClip _child2,int _weight,IScriptEnvironment* env); // Constructor
~MergeCPP(); // Destructor
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env); // GetFrame
};

// This is the destructor definition. This is called when the filter is destroyed.
MergeCPP::~MergeCPP()
{} // Empty Destructor

// This is the constructor.
MergeCPP::MergeCPP(PClip _child,PClip _child2,int _weight,IScriptEnvironment* env) :
GenericVideoFilter(_child),child2(_child2),weight(_weight)
{
VideoInfo vi2 = child2->GetVideoInfo();
double fps = (double)vi.fps_numerator/(double)vi.fps_denominator;

if(!vi.IsYUY2() || !vi2.IsYUY2())
env->ThrowError("Input video formats must both be: YUY2");
if((vi.width != vi2.width) || (vi.height != vi2.height))
env->ThrowError("Clips Dissimilar Dimensions");
if(fabs(fps-((double)vi2.fps_numerator/(double)vi2.fps_denominator)) > 0.000001)
env->ThrowError("Clips FrameRate MisMatch");
if(vi.num_frames != vi2.num_frames)
env->ThrowError("Dissimilar number of frames");
}

// GetFrame() Called by Avisynth to get frame n
PVideoFrame __stdcall MergeCPP::GetFrame(int n, IScriptEnvironment* env) {

// Established in constructor that both inputs are idential dimensions etc.

PVideoFrame s1_frame, s2_frame, d_frame;
const BYTE* s1_data,*s2_data;
BYTE * d_data;

int s1_pitch,s2_pitch,d_pitch; // Pitch can be different for all (even within same clip)
int height;
int rowsize;

s1_frame = child->GetFrame(n, env);
s2_frame = child2->GetFrame(n, env);
d_frame = env->NewVideoFrame(vi);

s1_data = s1_frame->GetReadPtr();
s2_data = s2_frame->GetReadPtr();
d_data = d_frame->GetWritePtr();

s1_pitch = s1_frame->GetPitch();
s2_pitch = s2_frame->GetPitch();
d_pitch = d_frame->GetPitch();
height = s1_frame->GetHeight(); // Heights are same
rowsize = s1_frame->GetRowSize(); // in bytes!

// TOTALLY IGNORED THE WEIGHT ARG, JUST STRAIGHT AVERAGE OF TWO PIXELS (should weight be float)
int x,y;
for(y=0 ; y < height; ++y) {
for(x=0; x < rowsize; ++x) {
d_data[x] = (s1_data[x] + s2_data[x]) / 2;
}
s1_data += s1_pitch;
s2_data += s2_pitch;
d_data += d_pitch;
}

return d_frame;
}

AVSValue __cdecl Create_MergeCPP(AVSValue args, void* user_data, IScriptEnvironment* env) {

return new MergeCPP( // Arguments to the Filter class Constructor of MergeCPP class.
args[0].AsClip(), // Clip no default
args[1].AsClip(), // Clip 2 no default
args[2].AsInt(1), // weight, defaults 1
env
);
}

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* env) {

env->AddFunction(
"MergeCPP", // The name of the filter in Avisynth.
"cc[weight]i", // Arguments, THIS MUST BE EDITED TO SUIT THE FILTER ARGUMENTS.
Create_MergeCPP, // The function Avisynth will call to create an instance of the filter.
0
);
return "`MergeCPP' MergeCPP plugin"; // A freeform name of the plugin.
}




EDIT: Could post the DemosaicCPP source if you wanna see it.
EDIT: I added a couple of additional checks to the constructor.

Gavino
11th November 2011, 11:00
class MergeCPP : public GenericVideoFilter {
private:
PClip child2;
int weight;
VideoInfo vi2;
...
};
vi2 could be made local to the constructor as it is used only there.

StainlessS
11th November 2011, 19:03
Thanks Gavino, amended.

Can you check out this for me please.


// ---------- avisynth.h interface extension - begin ----------
// The following is missing from avisynth.h version 3 and is needed by this plugin.
enum {V25_CS_Y8 = (1<<8 | VideoInfo::CS_YUV | VideoInfo::CS_PLANAR)}; // Y 4:0:0 planar};
bool V25_IsY8(VideoInfo&vix) const { return ((vix.pixel_type & V25_CS_Y8) == V25_CS_Y8);}
// ---------- avisynth_c interface extension - end ----------


It's what I modded in DemosaicCPP(), lives in the class.
Looks OK to me but I aint sure. :thanks:

Gavino
11th November 2011, 19:56
Can you check out this for me please.
It's what I modded in DemosaicCPP(), lives in the class.
Looks OK to me but I aint sure. :thanks:
I think it's OK.
It matches what was in the C code, and also the old 2.6 avisynth.h. The latest 2.6 avisynth.h expresses the properties in a different way, but I assume the values are equivalent (backwards compatible), though I'm not 100% sure.
Have you tried running it with Y8 input on the latest 2.6 build?

IanB
11th November 2011, 22:28
enum {V25_CS_Y8 = CS_PLANAR | CS_INTERLEAVED | CS_YUV | V25_CS_Sample_Bits_8};
... etc ..
bool V25_IsY8(VideoInfo&vix) const { return (vix.pixel_type & V25_CS_PLANAR_MASK) == (V25_CS_Y8 & V25_CS_PLANAR_FILTER); }

StainlessS
12th November 2011, 02:00
Hi guys. Have switched back to v2.6A3 and checked out C version Demosaic(), it does NOT accept Y8
as it is supposed to. Isolated the is_Y8 detection and tried in CPP version and it also fails.
Dug out both version 3 & 5 Avisynth.h and came up with this:-


// ---------- avisynth.h interface extension - begin ----------
// The following is missing from avisynth.h version 3 and is needed by this v2.5 plugin.
//
// From Avisynth.h Version 5
// CS_Shift_Sample_Bits = 16,
// CS_Sample_Bits_Mask = 7 << CS_Shift_Sample_Bits,
// CS_Sample_Bits_8 = 0 << CS_Shift_Sample_Bits,
//
enum {
V25_CS_Y = VideoInfo::CS_PLANAR | VideoInfo::CS_INTERLEAVED | VideoInfo::CS_YUV // Y 4:0:0 planar
};

bool V25_IsY8(VideoInfo&vix) const { return ((vix.pixel_type & (V25_CS_Y | (7 << 16))) == (V25_CS_Y | (0 <<16)));}
// ---------- avisynth_c interface extension - end ----------


Which seems to work just fine.
Having now seen previous post changed as suggested to this:


// ---------- avisynth.h interface extension - begin ----------
// The following is missing from avisynth.h version 3 and is needed by this v2.5 plugin.
//
// From Avisynth.h Version 5
// CS_Shift_Sample_Bits = 16,
// CS_Sample_Bits_Mask = 7 << CS_Shift_Sample_Bits,
// CS_Sample_Bits_8 = 0 << CS_Shift_Sample_Bits,
//
// Planar match mask 1111.0000.0000.0111.0000.0111.0000.0111 // 0xF0070707
// Planar signature 10xx.0000.0000.00xx.0000.00xx.00xx.00xx // 0xCFFCFCCC, 0x80000000
// Planar filter mask 1111.1111.1111.1111.1111.1111.1100.1111 // 0xFFFFFFCF
//
enum {
V25_CS_Shift_Sample_Bits = 16,
V25_CS_Sample_Bits_Mask = (7 << V25_CS_Shift_Sample_Bits),
V25_CS_Sample_Bits_8 = (0 << V25_CS_Shift_Sample_Bits),
V25_CS_PLANAR_MASK = 0xF0070707,
V25_CS_PLANAR_FILTER = 0xFFFFFFCF,
V25_CS_Y = VideoInfo::CS_PLANAR | VideoInfo::CS_INTERLEAVED | VideoInfo::CS_YUV,
V25_CS_Y8 = V25_CS_Y | V25_CS_Sample_Bits_8
};

bool V25_IsY8(VideoInfo&vix) const { return (vix.pixel_type & V25_CS_PLANAR_MASK) == (V25_CS_Y8 & V25_CS_PLANAR_FILTER);}
// ---------- avisynth_c interface extension - end ----------




Thankyou kind sirs. :)