1 //////////////////////////////////////////
2 // This file contains a simple invert
3 // filter that's commented to show
4 // the basics of the filter api.
5 // This file may make more sense when
6 // read from the bottom and up.
9 #include "VapourSynth.h"
14 const VSVideoInfo
*vi
;
18 // This function is called immediately after vsapi->createFilter(). This is the only place where the video
19 // properties may be set. In this case we simply use the same as the input clip. You may pass an array
20 // of VSVideoInfo if the filter has more than one output, like rgb+alpha as two separate clips.
21 static void VS_CC
invertInit(VSMap
*in
, VSMap
*out
, void **instanceData
, VSNode
*node
, VSCore
*core
, const VSAPI
*vsapi
) {
22 InvertData
*d
= (InvertData
*) * instanceData
;
23 vsapi
->setVideoInfo(d
->vi
, 1, node
);
26 // This is the main function that gets called when a frame should be produced. It will, in most cases, get
27 // called several times to produce one frame. This state is being kept track of by the value of
28 // activationReason. The first call to produce a certain frame n is always arInitial. In this state
29 // you should request all the input frames you need. Always do it in ascending order to play nice with the
31 // Once all frames are ready, the filter will be called with arAllFramesReady. It is now time to
32 // do the actual processing.
33 static const VSFrameRef
*VS_CC
invertGetFrame(int n
, int activationReason
, void **instanceData
, void **frameData
, VSFrameContext
*frameCtx
, VSCore
*core
, const VSAPI
*vsapi
) {
34 InvertData
*d
= (InvertData
*) * instanceData
;
36 if (activationReason
== arInitial
) {
37 // Request the source frame on the first call
38 vsapi
->requestFrameFilter(n
, d
->node
, frameCtx
);
39 } else if (activationReason
== arAllFramesReady
) {
40 const VSFrameRef
*src
= vsapi
->getFrameFilter(n
, d
->node
, frameCtx
);
41 // The reason we query this on a per frame basis is because we want our filter
42 // to accept clips with varying dimensions. If we reject such content using d->vi
44 const VSFormat
*fi
= d
->vi
->format
;
45 int height
= vsapi
->getFrameHeight(src
, 0);
46 int width
= vsapi
->getFrameWidth(src
, 0);
49 // When creating a new frame for output it is VERY EXTREMELY SUPER IMPORTANT to
50 // supply the "dominant" source frame to copy properties from. Frame props
51 // are an essential part of the filter chain and you should NEVER break it.
52 VSFrameRef
*dst
= vsapi
->newVideoFrame(fi
, width
, height
, src
, core
);
54 // It's processing loop time!
55 // Loop over all the planes
57 for (plane
= 0; plane
< fi
->numPlanes
; plane
++) {
58 const uint8_t *srcp
= vsapi
->getReadPtr(src
, plane
);
59 int src_stride
= vsapi
->getStride(src
, plane
);
60 uint8_t *dstp
= vsapi
->getWritePtr(dst
, plane
);
61 int dst_stride
= vsapi
->getStride(dst
, plane
); // note that if a frame has the same dimensions and format, the stride is guaranteed to be the same. int dst_stride = src_stride would be fine too in this filter.
62 // Since planes may be subsampled you have to query the height of them individually
63 int h
= vsapi
->getFrameHeight(src
, plane
);
65 int w
= vsapi
->getFrameWidth(src
, plane
);
68 for (y
= 0; y
< h
; y
++) {
69 for (x
= 0; x
< w
; x
++)
77 // Release the source frame
78 vsapi
->freeFrame(src
);
80 // A reference is consumed when it is returned, so saving the dst reference somewhere
81 // and reusing it is not allowed.
88 // Free all allocated data on filter destruction
89 static void VS_CC
invertFree(void *instanceData
, VSCore
*core
, const VSAPI
*vsapi
) {
90 InvertData
*d
= (InvertData
*)instanceData
;
91 vsapi
->freeNode(d
->node
);
95 // This function is responsible for validating arguments and creating a new filter
96 static void VS_CC
invertCreate(const VSMap
*in
, VSMap
*out
, void *userData
, VSCore
*core
, const VSAPI
*vsapi
) {
102 // Get a clip reference from the input arguments. This must be freed later.
103 d
.node
= vsapi
->propGetNode(in
, "clip", 0, 0);
104 d
.vi
= vsapi
->getVideoInfo(d
.node
);
106 // In this first version we only want to handle 8bit integer formats. Note that
107 // vi->format can be 0 if the input clip can change format midstream.
108 if (!isConstantFormat(d
.vi
) || d
.vi
->format
->sampleType
!= stInteger
|| d
.vi
->format
->bitsPerSample
!= 8) {
109 vsapi
->setError(out
, "Invert: only constant format 8bit integer input supported");
110 vsapi
->freeNode(d
.node
);
114 // If a property read fails for some reason (index out of bounds/wrong type)
115 // then err will have flags set to indicate why and 0 will be returned. This
116 // can be very useful to know when having optional arguments. Since we have
117 // strict checking because of what we wrote in the argument string, the only
118 // reason this could fail is when the value wasn't set by the user.
119 // And when it's not set we want it to default to enabled.
120 d
.enabled
= !!vsapi
->propGetInt(in
, "enable", 0, &err
);
124 // Let's pretend the only allowed values are 1 or 0...
125 if (d
.enabled
< 0 || d
.enabled
> 1) {
126 vsapi
->setError(out
, "Invert: enabled must be 0 or 1");
127 vsapi
->freeNode(d
.node
);
131 // I usually keep the filter data struct on the stack and don't allocate it
132 // until all the input validation is done.
133 data
= malloc(sizeof(d
));
136 // Creates a new filter and returns a reference to it. Always pass on the in and out
137 // arguments or unexpected things may happen. The name should be something that's
138 // easy to connect to the filter, like its function name.
139 // The three function pointers handle initialization, frame processing and filter destruction.
140 // The filtermode is very important to get right as it controls how threading of the filter
141 // is handled. In general you should only use fmParallel whenever possible. This is if you
142 // need to modify no shared data at all when the filter is running.
143 // For more complicated filters, fmParallelRequests is usually easier to achieve as it can
144 // be prefetched in parallel but the actual processing is serialized.
145 // The others can be considered special cases where fmSerial is useful to source filters and
146 // fmUnordered is useful when a filter's state may change even when deciding which frames to
147 // prefetch (such as a cache filter).
148 // If your filter is really fast (such as a filter that only resorts frames) you should set the
149 // nfNoCache flag to make the caching work smoother.
150 vsapi
->createFilter(in
, out
, "Invert", invertInit
, invertGetFrame
, invertFree
, fmParallel
, 0, data
, core
);
153 //////////////////////////////////////////
156 // This is the entry point that is called when a plugin is loaded. You are only supposed
157 // to call the two provided functions here.
158 // configFunc sets the id, namespace, and long name of the plugin (the last 3 arguments
159 // never need to be changed for a normal plugin).
161 // id: Needs to be a "reverse" url and unique among all plugins.
162 // It is inspired by how android packages identify themselves.
163 // If you don't own a domain then make one up that's related
164 // to the plugin name.
166 // namespace: Should only use [a-z_] and not be too long.
168 // full name: Any name that describes the plugin nicely.
170 // registerFunc is called once for each function you want to register. Function names
171 // should be PascalCase. The argument string has this format:
172 // name:type; or name:type:flag1:flag2....;
173 // All argument name should be lowercase and only use [a-z_].
174 // The valid types are int,float,data,clip,frame,func. [] can be appended to allow arrays
175 // of type to be passed (numbers:int[])
176 // The available flags are opt, to make an argument optional, empty, which controls whether
177 // or not empty arrays are accepted and link which will not be explained here.
179 VS_EXTERNAL_API(void) VapourSynthPluginInit(VSConfigPlugin configFunc
, VSRegisterFunction registerFunc
, VSPlugin
*plugin
) {
180 configFunc("com.example.invert", "invert", "VapourSynth Invert Example", VAPOURSYNTH_API_VERSION
, 1, plugin
);
181 registerFunc("Filter", "clip:clip;enabled:int:opt;", invertCreate
, 0, plugin
);