Filter SDK/CMerge

From Avisynth wiki
Revision as of 20:03, 29 December 2016 by Admin (Talk | contribs)

Jump to: navigation, search

Here is another working Avisynth C plugin. It's called "Merge", and it merges two clips according to a weight.

Here's Merge.c:

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

typedef struct Merge {
   AVS_Clip* clip2;
   double weight;
} Merge;

AVS_VideoFrame* AVSC_CC Merge_get_frame(AVS_FilterInfo* fi, int n)
{
   Merge* params = (Merge*) fi->user_data;

   AVS_VideoFrame* src = avs_get_frame(fi->child, n);
   const BYTE*     srcp = avs_get_read_ptr(src);
   int             src_pitch = avs_get_pitch(src);

   AVS_VideoFrame* src2 = avs_get_frame(params->clip2, n);
   const BYTE*     srcp2 = avs_get_read_ptr(src2);
   int             src2_pitch = avs_get_pitch(src2);

   AVS_VideoFrame* dst = avs_new_video_frame(fi->env, &fi->vi);
   BYTE*           dstp = avs_get_write_ptr(dst);
   int             dst_pitch = avs_get_pitch(dst);
   int             dst_rowsize = avs_get_row_size(dst); // in bytes!
   int             dst_height = avs_get_height(dst);

   int x, y, ch_tot;
   double w;

   if (avs_is_rgb32(&fi->vi))
      ch_tot = 4;
   else // rgb24
      ch_tot = 3;

   w = params->weight;

   for (y=0; y<dst_height; ++y) {
      for (x=0; x<dst_rowsize; x+=ch_tot) {
         dstp[x]   = (int)((1-w)*srcp[x] + w*srcp2[x]+0.5);     // B
         dstp[x+1] = (int)((1-w)*srcp[x+1] + w*srcp2[x+1]+0.5); // G
         dstp[x+2] = (int)((1-w)*srcp[x+2] + w*srcp2[x+2]+0.5); // R
      }
      srcp += src_pitch;
      srcp2 += src2_pitch;
      dstp += dst_pitch;
   }

   avs_release_frame(src);
   avs_release_frame(src2);

   return dst;
}

void AVSC_CC free_Merge(AVS_FilterInfo* fi)
{
   Merge* params = (Merge*) fi->user_data;
   avs_release_clip(params->clip2);
   free(params);
}

AVS_Value AVSC_CC create_Merge(AVS_ScriptEnvironment* env, AVS_Value args, void* user_data)
{
   AVS_Value v;
   AVS_Value tmp;
   AVS_FilterInfo* fi;
   const AVS_VideoInfo* vi2;

   AVS_Clip* new_clip = avs_new_c_filter(env, &fi, avs_array_elt(args, 0), 1);

   Merge *params = (Merge*)malloc(sizeof(Merge));
   if (!params)
      return avs_void;

   if (!avs_is_rgb(&fi->vi)) {
      return avs_new_value_error("Input video must be in RGB format!");
   }

   tmp = avs_array_elt(args, 1);
   if (avs_defined(tmp)) {
      params->clip2 = avs_take_clip(tmp, env);
      vi2 = avs_get_video_info(params->clip2);
      if (!avs_has_video(vi2)) {
         return avs_new_value_error("Second clip must be a video clip!");
      } 
      else if (!avs_is_color_space(vi2, fi->vi.pixel_type))
      {
         return avs_new_value_error("Second video must have the same pixel-type as the first clip!");
      } 
      else if (vi2->width != fi->vi.width || vi2->height != fi->vi.height)
      {
         return avs_new_value_error("Input and second clip sizes must match!");
      }
   } else {
      return avs_new_value_error("Second clip is missing!");
   }

   tmp = avs_array_elt(args, 2);
   if (avs_defined(tmp)) {
      params->weight = avs_as_float(tmp);
      if ((params->weight<0.0f) || (params->weight>1.0f)) {
         return avs_new_value_error("Make sure that '0.0 <= weight <= 1.0'!");
      }
   } else {
      params->weight = 0.5f; // default value
   }

   fi->user_data = (void*) params;
   fi->get_frame = Merge_get_frame;
   fi->free_filter = free_Merge;

   v = avs_new_value_clip(new_clip);

   avs_release_clip(new_clip);
   return v;
}

const char* AVSC_CC avisynth_c_plugin_init(AVS_ScriptEnvironment* env)
{
   avs_add_function(env, "Merge", "cc[weight]f", create_Merge, 0);
   return "Merge sample C plugin";
}

Line by line breakdown

Here's a line-by-line breakdown of Merge.c. I won't repeat the comments in the previous example InvertNeg.c, so read that first if needed. The declaration of the class is as follows

#include <stdlib.h> // free, malloc

This header defines free and malloc which we will need later on.

Note one could also include windows.h instead. However this gives a 'warning C4005: 'EXTERN_C' : macro redefinition' when compiling with MSVC++. I guess this needs to be fixed avisynth_c.h.

typedef struct Merge {
   AVS_Clip* clip2;
   double weight;
} Merge; // this name (ie Merge) is an alias for 'struct Merge'

In C we don't have classes. We will use a structure to declare our parameter variables (clip2 and weight in this example). The members of a structure are public by default (C++ plugins: recall that in a C++ class they are private by default).

typedef allows you to declare instances of a structure without using keyword "struct", since the second 'Merge' is an alias for 'struct Merge'. For more information have a look here and here.

AVS_VideoFrame* AVSC_CC Merge_get_frame(AVS_FilterInfo* fi, int n)

This method is called to make our filter produce frame n of its output.

   Merge* params = (Merge*) fi->user_data;

This fetches instance parameters.

   AVS_VideoFrame* src = avs_get_frame(fi->child, n);
   const BYTE*     srcp = avs_get_read_ptr(src);
   int             src_pitch = avs_get_pitch(src);

   AVS_VideoFrame* src2 = avs_get_frame(params->clip2, n);
   const BYTE*     srcp2 = avs_get_read_ptr(src2);
   int             src2_pitch = avs_get_pitch(src2);

Gives read pointers to both our input clips.

   AVS_VideoFrame* dst = avs_new_video_frame(fi->env, &fi->vi);

Creates a new frame according to what we set up in create_Merge.

   BYTE*           dstp = avs_get_write_ptr(dst);
   int             dst_pitch = avs_get_pitch(dst);
   int             dst_rowsize = avs_get_row_size(dst); // in bytes!
   int             dst_height = avs_get_height(dst);

Gives a write pointer to our output clips.

   if (avs_is_rgb32(&fi->vi))
      ch_tot = 4;
   else // rgb24
      ch_tot = 3;

This filter requires RGB24 or RGB32. ch_tot is the number of channels.

   w = params->weight;

The variable w is the input parameter weight. We will use that for better readability.

   for (y=0; y<dst_height; ++y) {
      for (x=0; x<dst_rowsize; x+=ch_tot) {
         dstp[x]   = (int)((1-w)*srcp[x] + w*srcp2[x]+0.5);     // B
         dstp[x+1] = (int)((1-w)*srcp[x+1] + w*srcp2[x+1]+0.5); // G
         dstp[x+2] = (int)((1-w)*srcp[x+2] + w*srcp2[x+2]+0.5); // R
      }
      srcp += src_pitch;
      srcp2 += src2_pitch;
      dstp += dst_pitch;
   }

This code does the actual work. It blends the red, green and blue channels together.

   avs_release_frame(src);
   avs_release_frame(src2);

Once we are done with the video frames of both clips they need to be released. Releasing a video frame does not actually delete it but decrements the reference count. (C++ plugins: recall this happens automatically when returning the destination frame.)

   return dst;

GetFrame returns the newly-created frame.

void AVSC_CC free_Merge(AVS_FilterInfo* fi)
{
   Merge* params = (Merge*) fi->user_data;
   avs_release_clip(params->clip2);
   free(params);
}

There are no constructors and deconstructors in C, but we need something to release memory. This is done by the function free_Merge which is defined above.

AVS_Value AVSC_CC create_Merge(AVS_ScriptEnvironment* env, AVS_Value args, void* user_data)

In order to use our new filter, we need a scripting-language function which creates an instance of it. This is that function.

   AVS_Clip* new_clip = avs_new_c_filter(env, &fi, avs_array_elt(args, 0), 1);

The new clip parameters are initialized from the input clip. fi is filled in according to the parent clip's parameters. You may modify fi afterwards to change the returned clip's parameters.

   Merge *params = (Merge*)malloc(sizeof(Merge));

Allocates memory and create a new instance of params.

   if (!params)
      return avs_void;

When failed to allocate memory, return avs_void.

   if (!avs_is_rgb(&fi->vi)) {
      return avs_new_value_error("Input video must be in RGB format!");
   }

...

   tmp = avs_array_elt(args, 1);
   if (avs_defined(tmp)) {
      params->clip2 = avs_take_clip(tmp, env);
      vi2 = avs_get_video_info(params->clip2);
      if (!avs_has_video(vi2)) {
         return avs_new_value_error("Second clip must be a video clip!");
      } 
      else if (!avs_is_color_space(vi2, fi->vi.pixel_type))
      {
         return avs_new_value_error("Second video must have the same pixel-type as the first clip!");
      } 
      else if (vi2->width != fi->vi.width || vi2->height != fi->vi.height)
      {
         return avs_new_value_error("Input and second clip sizes must match!");
      }
   } else {
      return avs_new_value_error("Second clip is missing!");
   }

   tmp = avs_array_elt(args, 2);
   if (avs_defined(tmp)) {
      params->weight = avs_as_float(tmp);
      if ((params->weight<0.0f) || (params->weight>1.0f)) {
         return avs_new_value_error("Make sure that '0.0 <= weight <= 1.0'!");
      }
   } else {
      params->weight = 0.5; // default value
   }

   fi->user_data = (void*) params;
   fi->get_frame = Merge_get_frame;
   fi->free_filter = free_Merge;

   v = avs_new_value_clip(new_clip);

   avs_release_clip(new_clip);
   return v;
}

const char* AVSC_CC avisynth_c_plugin_init(AVS_ScriptEnvironment* env)
{
   avs_add_function(env, "Merge", "cc[weight]f", create_Merge, 0);
   return "Merge sample C plugin";
}
Personal tools