Never call Py_Finalize()
[vapoursynth-svn.git] / sdk / invert_example.c
blobdfbc1837ef4e04097839b7961a556fef10566c85
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.
8 #include <stdlib.h>
9 #include "VapourSynth.h"
10 #include "VSHelper.h"
12 typedef struct {
13 VSNodeRef *node;
14 const VSVideoInfo *vi;
15 int enabled;
16 } InvertData;
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
30 // upstream filters.
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
43 // would be better.
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
56 int plane;
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);
64 int y;
65 int w = vsapi->getFrameWidth(src, plane);
66 int x;
68 for (y = 0; y < h; y++) {
69 for (x = 0; x < w; x++)
70 dstp[x] = ~srcp[x];
72 dstp += dst_stride;
73 srcp += src_stride;
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.
82 return dst;
85 return 0;
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);
92 free(d);
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) {
97 InvertData d;
98 InvertData *data;
99 VSNodeRef *cref;
100 int err;
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);
111 return;
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);
121 if (err)
122 d.enabled = 1;
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);
128 return;
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));
134 *data = 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);
151 return;}
153 //////////////////////////////////////////
154 // Init
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);