// This function is responsible for validating arguments and creating a new filter
static void VS_CC invertCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
    InvertData d;
    InvertData *data;
    VSNodeRef *cref;
    int err;

    // Get a clip reference from the input arguments. This must be freed later.
    d.node = vsapi->propGetNode(in, "clip", 0, 0);
    d.vi = vsapi->getVideoInfo(d.node);

    // In this first version we only want to handle 8bit integer formats. Note that
    // vi->format can be 0 if the input clip can change format midstream.
    if (!isConstantFormat(d.vi) || d.vi->format->sampleType != stInteger || d.vi->format->bitsPerSample != 8) {
        vsapi->setError(out, "Invert: only constant format 8bit integer input supported");
        vsapi->freeNode(d.node);
        return;
    }

    // If a property read fails for some reason (index out of bounds/wrong type)
    // then err will have flags set to indicate why and 0 will be returned. This
    // can be very useful to know when having optional arguments. Since we have
    // strict checking because of what we wrote in the argument string the only reason
    // this could fail is when the value wasn't set by the user.
    // And when it's not set we want it to default to enabled.
    d.enabled = !!vsapi->propGetInt(in, "enable", 0, &err);
    if (err)
        d.enabled = 1;

    // Let's pretend the only allowed values are 1 or 0...
    if (d.enabled < 0 || d.enabled > 1) {
        vsapi->setError(out, "Invert: enabled must be 0 or 1");
        vsapi->freeNode(d.node);
        return;
    }

    // I usually keep the filter data struct on the stack and don't allocate it
    // until all the input validation is done.
    data = malloc(sizeof(d));
    *data = d;

    // Create a new filter and returns a reference to it. Always pass on the in and out
    // arguments or unexpected things may happen. The name should be something that's
    // easy to connect to the filter, like its function name.
    // The three function pointers handle initialization, frame processing and filter destruction.
    // The filtermode is very important to get right as it controls how threading of the filter
    // is handled. In general you should only use fmParallel whenever possible. This is if you
    // need to modify no shared data at all when the filter is running.
    // For more complicated filters fmParallelRequests is usually easier to achieve as an
    // be prefetched in parallel but the actual processing is serialized.
    // The others can be considered special cases where fmSerial is useful to source filters and
    // fmUnordered is useful when a filter's state may change even when deciding which frames to
    // prefetch (such as a cache filter).
    // If you filter is really fast (such as a filter that only resorts frames) you should set the
    // nfNoCache flag to make the caching work smoother.
    vsapi->createFilter(in, out, "Invert", invertInit, invertGetFrame, invertFree, fmParallel, 0, data, core);
    return;
}
Beispiel #2
0
static int get_video_track
(
    lsmash_handler_t *h
)
{
    vpy_handler_t *hp = (vpy_handler_t *)h->video_private;
    if( !isConstantFormat( hp->vi ) || hp->vi->numFrames <= 0 )
        return -1;
    hp->av_frame = av_frame_alloc();
    return hp->av_frame ? 0 : -1;
}
Beispiel #3
0
static void VS_CC minsrpCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
	MinSRPData d;
	MinSRPData *data = nullptr;
	d.node = vsapi->propGetNode(in, "clip", 0, 0);
	d.vi = vsapi->getVideoInfo(d.node);
	const int m = vsapi->propNumElements(in, "mode");
	const int n = vsapi->propNumElements(in, "str");
	if (m > d.vi->format->numPlanes) {
		vsapi->setError(out, "MinSRP: number of modes specified must be equal to or fewer than the number of input planes");
		vsapi->freeNode(d.node);
		return;
	}
	if (n > d.vi->format->numPlanes) {
		vsapi->setError(out, "MinSRP: number of the specified elements in str array must be equal to or fewer than the number of input planes");
		vsapi->freeNode(d.node);
		return;
	}
	for (int i = 0; i < 3; ++i) {
		if (m == -1)
			d.mode[0] = d.mode[1] = d.mode[2] = 3;
		else
			if (i < m) {
				d.mode[i] = int64ToIntS(vsapi->propGetInt(in, "mode", i, nullptr));
				if (d.mode[i] < 0 || d.mode[i] > 3) {
					vsapi->setError(out, "MinSRP: invalid mode specified, only modes 0-3 supported");
					vsapi->freeNode(d.node);
					return;
				}
			}
			else
				d.mode[i] = d.mode[i - 1];
		if (n == -1)
			d.str[0] = d.str[1] = d.str[2] = 1.;
		else
			if (i < n)
				d.str[i] = vsapi->propGetFloat(in, "str", i, nullptr);
			else
				d.str[i] = d.str[i - 1];
		if (d.vi->format->subSamplingH || d.vi->format->subSamplingW) {
			vsapi->setError(out, "MinSRP: 4:4:4 or gray input required!");
			vsapi->freeNode(d.node);
			return;
		}
	}
	if (!isConstantFormat(d.vi)) {
		vsapi->setError(out, "MinSRP: only input with constant format supported");
		vsapi->freeNode(d.node);
		return;
	}
	data = new MinSRPData;
	*data = d;
	vsapi->createFilter(in, out, "MinSRP", minsrpInit, minsrpGetFrame, minsrpFree, fmParallel, 0, data, core);
}
Beispiel #4
0
void VS_CC SCSelectCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi)
{
    SCSelectData d = { 0 };

    d.input = vsapi->propGetNode(in, "input", 0, 0);
    d.vi = vsapi->getVideoInfo(d.input);

    if (!isConstantFormat(d.vi)) {
        vsapi->freeNode(d.input);
        vsapi->setError(out, "SCSelect: Only constant format input supported");
        return;
    }

    if (d.vi->format->id != pfYUV420P8 && d.vi->format->id != pfYUV422P8) {
        vsapi->freeNode(d.input);
        vsapi->setError(out, "SCSelect: Only planar YV12 and YUY2 colorspaces are supported");
        return;
    }

    d.sceneBegin = vsapi->propGetNode(in, "sceneBegin", 0, 0);
    d.sceneEnd = vsapi->propGetNode(in, "sceneEnd", 0, 0);
    d.globalMotion = vsapi->propGetNode(in, "globalMotion", 0, 0);

    if (!isSameFormat(d.vi, vsapi->getVideoInfo(d.sceneBegin)) ||
        !isSameFormat(d.vi, vsapi->getVideoInfo(d.sceneEnd)) ||
        !isSameFormat(d.vi, vsapi->getVideoInfo(d.globalMotion))) {
            vsapi->freeNode(d.input);
            vsapi->freeNode(d.sceneBegin);
            vsapi->freeNode(d.sceneEnd);
            vsapi->freeNode(d.globalMotion);
            vsapi->setError(out, "SCSelect: Clips are not of equal type");
            return;
    }

    int32_t err;
    double dFactor = vsapi->propGetFloat(in, "dfactor", 0, &err);
    if (err) {
        dFactor = 4.0;
    }

    d.hblocks = d.vi->width / (2 * 16);
    d.incpitch = d.hblocks * (-2 * 16);

    SCSelectData *data = (SCSelectData *)malloc(sizeof(d));
    if (!data) {
        vsapi->setError(out, "Could not allocate SCSelectData");
        return;
    }
    *data = d;

    vsapi->createFilter(in, out, "SCSelect", SCSelectInit, SCSelectGetFrame, SCSelectFree, fmParallel, 0, data, core);
};
Beispiel #5
0
static void VS_CC infiltInit(VSMap *in, VSMap *out, void **instanceData,
                             VSNode *node, VSCore *core, const VSAPI *vsapi)
{
    struct vf_instance *vf = *instanceData;
    struct vf_priv_s *p = vf->priv;

    // Note: this is called from createFilter, so no need for locking.

    VSVideoInfo fmt = {
        .format = p->vsapi->getFormatPreset(mp_to_vs(p->fmt_in.imgfmt), p->vscore),
        .width = p->fmt_in.w,
        .height = p->fmt_in.h,
    };
    if (!fmt.format) {
        p->vsapi->setError(out, "Unsupported input format.\n");
        return;
    }

    p->vsapi->setVideoInfo(&fmt, 1, node);
    p->in_node_active = true;
}

static const VSFrameRef *VS_CC infiltGetFrame(int frameno, int activationReason,
    void **instanceData, void **frameData,
    VSFrameContext *frameCtx, VSCore *core,
    const VSAPI *vsapi)
{
    struct vf_instance *vf = *instanceData;
    struct vf_priv_s *p = vf->priv;
    VSFrameRef *ret = NULL;

    pthread_mutex_lock(&p->lock);
    while (1) {
        if (p->shutdown)
            break;
        if (frameno < p->in_frameno) {
            p->vsapi->setFilterError("Requesting a frame too far in the past. "
                                     "Try increasing the maxbuffer suboption",
                                     frameCtx);
            break;
        }
        if (frameno >= p->in_frameno + MP_TALLOC_ELEMS(p->buffered)) {
            // Too far in the future. Remove frames, so that the main thread can
            // queue new frames.
            if (p->num_buffered) {
                drain_oldest_buffered_frame(p);
                pthread_cond_broadcast(&p->wakeup);
                continue;
            }
        }
        if (frameno < p->in_frameno + p->num_buffered) {
            struct mp_image *img = p->buffered[frameno - p->in_frameno];
            const VSFormat *vsfmt =
                vsapi->getFormatPreset(mp_to_vs(img->imgfmt), core);
            ret = vsapi->newVideoFrame(vsfmt, img->w, img->h, NULL, core);
            if (!ret) {
                p->vsapi->setFilterError("Could not allocate VS frame", frameCtx);
                break;
            }
            struct mp_image vsframe = map_vs_frame(p, ret, true);
            mp_image_copy(&vsframe, img);
            VSMap *map = p->vsapi->getFramePropsRW(ret);
            if (map) {
                int res = 1e6;
                int dur = img->pts * res + 0.5;
                p->vsapi->propSetInt(map, "_DurationNum", dur, 0);
                p->vsapi->propSetInt(map, "_DurationDen", res, 0);
                copy_mp_to_vs_frame_props(p, map, img);
            }
            break;
        }
        pthread_cond_wait(&p->wakeup, &p->lock);
    }
    pthread_mutex_unlock(&p->lock);
    return ret;
}

static void VS_CC infiltFree(void *instanceData, VSCore *core, const VSAPI *vsapi)
{
    struct vf_instance *vf = instanceData;
    struct vf_priv_s *p = vf->priv;

    pthread_mutex_lock(&p->lock);
    p->in_node_active = false;
    pthread_cond_broadcast(&p->wakeup);
    pthread_mutex_unlock(&p->lock);
}

static void destroy_vs(struct vf_instance *vf)
{
    struct vf_priv_s *p = vf->priv;

    // Wait until our frame callback returns.
    pthread_mutex_lock(&p->lock);
    p->shutdown = true;
    pthread_cond_broadcast(&p->wakeup);
    while (p->getting_frame)
        pthread_cond_wait(&p->wakeup, &p->lock);
    pthread_mutex_unlock(&p->lock);

    if (p->in_node)
        p->vsapi->freeNode(p->in_node);
    if (p->out_node)
        p->vsapi->freeNode(p->out_node);
    p->in_node = p->out_node = NULL;

    if (p->se)
        vsscript_freeScript(p->se);

    p->se = NULL;
    p->vsapi = NULL;
    p->vscore = NULL;

    assert(!p->in_node_active);

    p->shutdown = false;
    talloc_free(p->got_frame);
    p->got_frame = NULL;
    // Kill queued frames too
    for (int n = 0; n < p->num_buffered; n++)
        talloc_free(p->buffered[n]);
    p->num_buffered = 0;
    talloc_free(p->next_image);
    p->next_image = NULL;
    p->prev_pts = MP_NOPTS_VALUE;
    p->out_frameno = p->in_frameno = 0;
}

static int reinit_vs(struct vf_instance *vf)
{
    struct vf_priv_s *p = vf->priv;
    VSMap *vars = NULL, *in = NULL, *out = NULL;
    int res = -1;

    destroy_vs(vf);

    // First load an empty script to get a VSScript, so that we get the vsapi
    // and vscore.
    if (vsscript_evaluateScript(&p->se, "", NULL, 0))
        goto error;
    p->vsapi = vsscript_getVSApi();
    p->vscore = vsscript_getCore(p->se);
    if (!p->vsapi || !p->vscore)
        goto error;

    in = p->vsapi->createMap();
    out = p->vsapi->createMap();
    vars = p->vsapi->createMap();
    if (!in || !out || !vars)
        goto error;

    p->vsapi->createFilter(in, out, "Input", infiltInit, infiltGetFrame,
                           infiltFree, fmSerial, 0, vf, p->vscore);
    int vserr;
    p->in_node = p->vsapi->propGetNode(out, "clip", 0, &vserr);
    if (!p->in_node)
        goto error;

    if (p->vsapi->propSetNode(vars, "video_in", p->in_node, 0))
        goto error;

    vsscript_setVariable(p->se, vars);

    if (vsscript_evaluateFile(&p->se, p->cfg_file, 0)) {
        MP_FATAL(vf, "Script evaluation failed:\n%s\n", vsscript_getError(p->se));
        goto error;
    }
    p->out_node = vsscript_getOutput(p->se, 0);
    if (!p->out_node)
        goto error;

    const VSVideoInfo *vi = p->vsapi->getVideoInfo(p->out_node);
    if (!isConstantFormat(vi)) {
        MP_FATAL(vf, "Video format is required to be constant.\n");
        goto error;
    }

    res = 0;
error:
    if (p->vsapi) {
        p->vsapi->freeMap(in);
        p->vsapi->freeMap(out);
        p->vsapi->freeMap(vars);
    }
    if (res < 0)
        destroy_vs(vf);
    return res;
}

static int config(struct vf_instance *vf, int width, int height,
                  int d_width, int d_height, unsigned int flags,
                  unsigned int fmt)
{
    struct vf_priv_s *p = vf->priv;

    p->fmt_in = (struct mp_image_params){
        .imgfmt = fmt,
        .w = width,
        .h = height,
    };

    if (reinit_vs(vf) < 0)
        return 0;

    const VSVideoInfo *vi = p->vsapi->getVideoInfo(p->out_node);
    fmt = mp_from_vs(vi->format->id);
    if (!fmt) {
        MP_FATAL(vf, "Unsupported output format.\n");
        destroy_vs(vf);
        return 0;
    }

    vf_rescale_dsize(&d_width, &d_height, width, height, vi->width, vi->height);

    return vf_next_config(vf, vi->width, vi->height, d_width, d_height, flags, fmt);
}

static int query_format(struct vf_instance *vf, unsigned int fmt)
{
    return mp_to_vs(fmt) != pfNone ? VFCAP_CSP_SUPPORTED : 0;
}

static int control(vf_instance_t *vf, int request, void *data)
{
    switch (request) {
    case VFCTRL_SEEK_RESET:
        if (reinit_vs(vf) < 0)
            return CONTROL_ERROR;
        return CONTROL_OK;
    }
    return CONTROL_UNKNOWN;
}

static void uninit(struct vf_instance *vf)
{
    struct vf_priv_s *p = vf->priv;

    destroy_vs(vf);
    vsscript_finalize();

    pthread_cond_destroy(&p->wakeup);
    pthread_mutex_destroy(&p->lock);
}

static int vf_open(vf_instance_t *vf)
{
    struct vf_priv_s *p = vf->priv;
    if (!vsscript_init()) {
        MP_FATAL(vf, "Could not initialize VapourSynth scripting.\n");
        return 0;
    }
    if (!p->cfg_file || !p->cfg_file[0]) {
        MP_FATAL(vf, "'file' parameter must be set.\n");
        return 0;
    }

    pthread_mutex_init(&p->lock, NULL);
    pthread_cond_init(&p->wakeup, NULL);
    vf->reconfig = NULL;
    vf->config = config;
    vf->filter_ext = filter_ext;
    vf->filter = NULL;
    vf->query_format = query_format;
    vf->control = control;
    vf->uninit = uninit;
    p->buffered = talloc_array(vf, struct mp_image *, p->cfg_maxbuffer);
    return 1;
}

#define OPT_BASE_STRUCT struct vf_priv_s
static const m_option_t vf_opts_fields[] = {
    OPT_STRING("file", cfg_file, 0),
    OPT_INTRANGE("maxbuffer", cfg_maxbuffer, 0, 1, 9999, OPTDEF_INT(5)),
    {0}
};

const vf_info_t vf_info_vapoursynth = {
    .description = "vapoursynth bridge",
    .name = "vapoursynth",
    .open = vf_open,
    .priv_size = sizeof(struct vf_priv_s),
    .options = vf_opts_fields,
};
Beispiel #6
0
static void VS_CC mvsuperCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
	MVSuperData d;
	MVSuperData *data;
	int err;
	d.nHPad = int64ToIntS(vsapi->propGetInt(in, "hpad", 0, &err));
	if (err)
		d.nHPad = 8;
	d.nVPad = int64ToIntS(vsapi->propGetInt(in, "vpad", 0, &err));
	if (err)
		d.nVPad = 8;
	d.nPel = int64ToIntS(vsapi->propGetInt(in, "pel", 0, &err));
	if (err)
		d.nPel = 2;
	d.nLevels = int64ToIntS(vsapi->propGetInt(in, "levels", 0, &err));
	d.chroma = !!vsapi->propGetInt(in, "chroma", 0, &err);
	if (err)
		d.chroma = 1;
	d.sharp = int64ToIntS(vsapi->propGetInt(in, "sharp", 0, &err));
	if (err)
		d.sharp = 2;
	d.rfilter = int64ToIntS(vsapi->propGetInt(in, "rfilter", 0, &err));
	if (err)
		d.rfilter = 2;
	if ((d.nPel != 1) && (d.nPel != 2) && (d.nPel != 4)) {
		vsapi->setError(out, "Super: pel must be 1, 2, or 4.");
		return;
	}
	if (d.sharp < 0 || d.sharp > 2) {
		vsapi->setError(out, "Super: sharp must be between 0 and 2 (inclusive).");
		return;
	}
	if (d.rfilter < 0 || d.rfilter > 4) {
		vsapi->setError(out, "Super: rfilter must be between 0 and 4 (inclusive).");
		return;
	}
	d.node = vsapi->propGetNode(in, "clip", 0, 0);
	d.vi = *vsapi->getVideoInfo(d.node);
	d.nWidth = d.vi.width;
	d.nHeight = d.vi.height;
	if (!isConstantFormat(&d.vi) || d.vi.format->bitsPerSample < 32 || d.vi.format->sampleType != stFloat) {
		vsapi->setError(out, "Super: input clip must be single precision fp, with constant dimensions.");
		vsapi->freeNode(d.node);
		return;
	}
	if (d.vi.format->colorFamily == cmGray)
		d.chroma = 0;
	if (d.vi.format->colorFamily == cmRGB)
		d.chroma = 1;
	d.nModeYUV = d.chroma ? YUVPLANES : YPLANE;
	d.xRatioUV = 1 << d.vi.format->subSamplingW;
	d.yRatioUV = 1 << d.vi.format->subSamplingH;
	int32_t nLevelsMax = 0;
	while (PlaneHeightLuma(d.vi.height, nLevelsMax, d.yRatioUV, d.nVPad) >= d.yRatioUV * 2 &&
		PlaneWidthLuma(d.vi.width, nLevelsMax, d.xRatioUV, d.nHPad) >= d.xRatioUV * 2) {
		++nLevelsMax;
	}
	if (d.nLevels <= 0 || d.nLevels > nLevelsMax)
		d.nLevels = nLevelsMax;
	d.pelclip = vsapi->propGetNode(in, "pelclip", 0, &err);
	const VSVideoInfo *pelvi = d.pelclip ? vsapi->getVideoInfo(d.pelclip) : nullptr;
	if (d.pelclip && (!isConstantFormat(pelvi) || pelvi->format != d.vi.format)) {
		vsapi->setError(out, "Super: pelclip must have the same format as the input clip, and it must have constant dimensions.");
		vsapi->freeNode(d.node);
		vsapi->freeNode(d.pelclip);
		return;
	}
	d.usePelClip = false;
	if (d.pelclip && (d.nPel >= 2)) {
		if ((pelvi->width == d.vi.width * d.nPel) &&
			(pelvi->height == d.vi.height * d.nPel)) {
			d.usePelClip = true;
			d.isPelClipPadded = false;
		}
		else if ((pelvi->width == (d.vi.width + d.nHPad * 2) * d.nPel) &&
			(pelvi->height == (d.vi.height + d.nVPad * 2) * d.nPel)) {
			d.usePelClip = true;
			d.isPelClipPadded = true;
		}
		else {
			vsapi->setError(out, "Super: pelclip's dimensions must be multiples of the input clip's dimensions.");
			vsapi->freeNode(d.pelclip);
			vsapi->freeNode(d.node);
			return;
		}
	}
	d.nSuperWidth = d.nWidth + 2 * d.nHPad;
	d.nSuperHeight = PlaneSuperOffset(false, d.nHeight, d.nLevels, d.nPel, d.nVPad, d.nSuperWidth, d.yRatioUV) / d.nSuperWidth;
	if (d.yRatioUV == 2 && d.nSuperHeight & 1)
		++d.nSuperHeight;
	if (d.xRatioUV == 2 && d.nSuperWidth & 1)
		++d.nSuperWidth;
	d.vi.width = d.nSuperWidth;
	d.vi.height = d.nSuperHeight;
	data = new MVSuperData;
	*data = d;
	vsapi->createFilter(in, out, "Super", mvsuperInit, mvsuperGetFrame, mvsuperFree, fmParallel, 0, data, core);
}
Beispiel #7
0
static void VS_CC resizeCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
    ResizeData d;
    ResizeData *data;
    int id;
    int dstwidth;
    int dstheight;
    int pf;
    int err;
    d.context = 0;
    d.dstrange = 0;
    d.lsrcformat = 0;
    d.lsrch = 0;
    d.lsrcw = 0;
    d.node = 0;
    d.flags = (intptr_t)userData;
    d.node = vsapi->propGetNode(in, "clip", 0, 0);
    d.vi = *vsapi->getVideoInfo(d.node);
    dstwidth = int64ToIntS(vsapi->propGetInt(in, "width", 0, &err));

    if (err)
        dstwidth = d.vi.width;

    dstheight = int64ToIntS(vsapi->propGetInt(in, "height", 0, &err));

    if (err)
        dstheight = d.vi.height;

    id = int64ToIntS(vsapi->propGetInt(in, "format", 0, &err));

    if (err && d.vi.format)
        id = d.vi.format->id;

    if (dstwidth > 0)
        d.vi.width = dstwidth;

    if (dstheight > 0)
        d.vi.height = dstheight;

    pf = formatIdToPixelFormat(id);

    if (pf == PIX_FMT_NONE) {
        vsapi->freeNode(d.node);
        RETERROR("Resize: unsupported output format");
    }

    d.vi.format = vsapi->getFormatPreset(id, core);

    if ((d.vi.width % (1 << d.vi.format->subSamplingW)) || (d.vi.height % (1 << d.vi.format->subSamplingH))) {
        vsapi->freeNode(d.node);
        RETERROR("Resize: mod requirements of the target colorspace not fulfilled");
    }

    if (!isConstantFormat(&d.vi)) {
        vsapi->freeNode(d.node);
        RETERROR("Resize: output format not constant, set width, height and format");
    }

    data = malloc(sizeof(d));
    *data = d;

    vsapi->createFilter(in, out, "Resize", resizeInit, resizeGetframe, resizeFree, fmParallelRequests, 0, data, core);
}
Beispiel #8
0
static int reinit_vs(struct vf_instance *vf)
{
    struct vf_priv_s *p = vf->priv;
    VSMap *vars = NULL, *in = NULL, *out = NULL;
    int res = -1;

    destroy_vs(vf);

    MP_DBG(vf, "initializing...\n");
    p->initializing = true;

    if (p->drv->load_core(vf) < 0 || !p->vsapi || !p->vscore) {
        MP_FATAL(vf, "Could not get vapoursynth API handle.\n");
        goto error;
    }

    in = p->vsapi->createMap();
    out = p->vsapi->createMap();
    vars = p->vsapi->createMap();
    if (!in || !out || !vars)
        goto error;

    p->vsapi->createFilter(in, out, "Input", infiltInit, infiltGetFrame,
                           infiltFree, fmSerial, 0, vf, p->vscore);
    int vserr;
    p->in_node = p->vsapi->propGetNode(out, "clip", 0, &vserr);
    if (!p->in_node) {
        MP_FATAL(vf, "Could not get our own input node.\n");
        goto error;
    }

    if (p->vsapi->propSetNode(vars, "video_in", p->in_node, 0))
        goto error;

    int d_w, d_h;
    mp_image_params_get_dsize(&p->fmt_in, &d_w, &d_h);

    p->vsapi->propSetInt(vars, "video_in_dw", d_w, 0);
    p->vsapi->propSetInt(vars, "video_in_dh", d_h, 0);
    p->vsapi->propSetFloat(vars, "container_fps", vf->chain->container_fps, 0);
    p->vsapi->propSetFloat(vars, "display_fps", vf->chain->display_fps, 0);

    if (p->drv->load(vf, vars) < 0)
        goto error;
    if (!p->out_node) {
        MP_FATAL(vf, "Could not get script output node.\n");
        goto error;
    }

    const VSVideoInfo *vi = p->vsapi->getVideoInfo(p->out_node);
    if (!isConstantFormat(vi)) {
        MP_FATAL(vf, "Video format is required to be constant.\n");
        goto error;
    }

    pthread_mutex_lock(&p->lock);
    p->initializing = false;
    pthread_mutex_unlock(&p->lock);
    MP_DBG(vf, "initialized.\n");
    res = 0;
error:
    if (p->vsapi) {
        p->vsapi->freeMap(in);
        p->vsapi->freeMap(out);
        p->vsapi->freeMap(vars);
    }
    if (res < 0)
        destroy_vs(vf);
    return res;
}
Beispiel #9
0
static void VS_CC vs_depth_create(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi)
{
	vs_depth_data *data = 0;
	zimg_depth_params params;
	zimg_filter *filter = 0;
	zimg_filter *filter_uv = 0;
	char fail_str[1024];
	int err;

	VSNodeRef *node = 0;
	const VSVideoInfo *node_vi;
	const VSFormat *node_fmt;
	VSVideoInfo vi;

	const char *dither_str;
	int sample_type;
	int depth;

	node = vsapi->propGetNode(in, "clip", 0, 0);
	node_vi = vsapi->getVideoInfo(node);
	node_fmt = node_vi->format;

	if (!isConstantFormat(node_vi)) {
		strcpy(fail_str, "clip must have a defined format");
		goto fail;
	}

	sample_type = (int)propGetIntDefault(vsapi, in, "sample", 0, node_fmt->sampleType);
	depth = (int)propGetIntDefault(vsapi, in, "depth", 0, node_fmt->bitsPerSample);

	if (sample_type != stInteger && sample_type != stFloat) {
		strcpy(fail_str, "invalid sample type: must be stInteger or stFloat");
		goto fail;
	}
	if (sample_type == stFloat && depth != 16 && depth != 32) {
		strcpy(fail_str, "invalid depth: must be 16 or 32 for stFloat");
		goto fail;
	}
	if (sample_type == stInteger && (depth <= 0 || depth > 16)) {
		strcpy(fail_str, "invalid depth: must be between 1-16 for stInteger");
		goto fail;
	}

	vi = *node_vi;
	vi.format = vsapi->registerFormat(node_fmt->colorFamily, sample_type, depth < 8 ? 8 : depth, node_fmt->subSamplingW, node_fmt->subSamplingH, core);

	if (!vi.format) {
		strcpy(fail_str, "unable to register output VSFormat");
		goto fail;
	}

	zimg2_depth_params_default(&params, ZIMG_API_VERSION);

	params.width = node_vi->width;
	params.height = node_vi->height;

	dither_str = vsapi->propGetData(in, "dither", 0, &err);
	if (!err)
		params.dither_type = translate_dither(dither_str);

	params.chroma = 0;

	params.pixel_in = translate_pixel(node_fmt);
	params.depth_in = node_fmt->bitsPerSample;
	params.range_in = (int)propGetIntDefault(vsapi, in, "range_in", 0, node_fmt->colorFamily == cmRGB ? ZIMG_RANGE_FULL : ZIMG_RANGE_LIMITED);

	params.pixel_out = translate_pixel(vi.format);
	params.depth_out = depth;
	params.range_out = (int)propGetIntDefault(vsapi, in, "range_out", 0, params.range_in);

	if (!(filter = zimg2_depth_create(&params))) {
		zimg_get_last_error(fail_str, sizeof(fail_str));
		goto fail;
	}

	if (node_fmt->colorFamily == cmYUV || node_fmt->colorFamily == cmYCoCg) {
		params.width = params.width >> node_fmt->subSamplingW;
		params.height = params.height >> node_fmt->subSamplingH;
		params.chroma = 1;

		if (!(filter_uv = zimg2_depth_create(&params))) {
			zimg_get_last_error(fail_str, sizeof(fail_str));
			goto fail;
		}
	}
Beispiel #10
0
int wmain(int argc, wchar_t **argv) {
#else
int main(int argc, char **argv) {
#endif

    if (argc < 3) {
        fprintf(stderr, "VSPipe usage:\n");
		fprintf(stderr, "Show script info: vspipe script.vpy - -info\n");
        fprintf(stderr, "Write to stdout: vspipe script.vpy - [options]\n");
        fprintf(stderr, "Write to file: vspipe script.vpy <outFile> [options]\n");
        fprintf(stderr, "Available options:\n");
		fprintf(stderr, "Select output index: -index N\n");
		fprintf(stderr, "Set number of concurrent frame requests: -requests N\n");
		fprintf(stderr, "Add YUV4MPEG headers: -y4m\n");
		fprintf(stderr, "Show video info: -info (overrides other options)\n");
        return 1;
    }

	QFile scriptFile(nativeToQString(argv[1]));
	if (!scriptFile.open(QIODevice::ReadOnly)) {
        fprintf(stderr, "Failed to to open script file for reading\n");
        return 1;
	}

	if (scriptFile.size() > 1024*1024*16) {
        fprintf(stderr, "Script files bigger than 16MB not allowed\n");
        return 1;
	}

    QByteArray scriptData = scriptFile.readAll();
	scriptFile.close();
    if (scriptData.isEmpty()) {
        fprintf(stderr, "Failed to read script file or file is empty\n");
        return 1;
    }

	QString outputFilename = nativeToQString(argv[2]);
	if (outputFilename == "-") {
		outFile = stdout;
	} else {
#ifdef VS_TARGET_OS_WINDOWS
		outFile = _wfopen(outputFilename.toStdWString().c_str(), L"wb");
#else
		outFile = fopen(outputFilename.toLocal8Bit(), "wb");
#endif
		if (!outFile) {
			fprintf(stderr, "Failed to open output for writing\n");
			return 1;
		}
	}

	for (int arg = 3; arg < argc; arg++) {
		QString argString = nativeToQString(argv[arg]);
		if (argString == "-y4m") {
			y4m = true;
		} else if (argString == "-info") {
			showInfo = true;
		} else if (argString == "-index") {
			bool ok = false;
			if (argc <= arg + 1) {
				fprintf(stderr, "No index number specified");
				return 1;
			}
			QString numString = nativeToQString(argv[arg+1]);
			outputIndex = numString.toInt(&ok);
			if (!ok) {
				fprintf(stderr, "Couldn't convert %s to an integer", numString.toUtf8().constData());
				return 1;
			}
			arg++;
		} else if (argString == "-requests") {
			bool ok = false;
			if (argc <= arg + 1) {
				fprintf(stderr, "No request number specified");
				return 1;
			}
			QString numString = nativeToQString(argv[arg+1]);
			requests = numString.toInt(&ok);
			if (!ok) {
				fprintf(stderr, "Couldn't convert %s to an integer", numString.toUtf8().constData());
				return 1;
			}
			arg++;
		} else {
			fprintf(stderr, "Unknown argument: %s\n", argString.toUtf8().constData());
			return 1;
		}
	}

    if (!vseval_init()) {
        fprintf(stderr, "Failed to initialize VapourSynth environment\n");
        return 1;
    }

    vsapi = vseval_getVSApi();
    if (!vsapi) {
        fprintf(stderr, "Failed to get VapourSynth API pointer\n");
        vseval_finalize();
        return 1;
    }

	if (vseval_evaluateScript(&se, scriptData.constData(), nativeToQString(argv[1]).toUtf8())) {
        fprintf(stderr, "Script evaluation failed:\n%s", vseval_getError(se));
        vseval_freeScript(se);
        vseval_finalize();
        return 1;
    }

    node = vseval_getOutput(se, outputIndex);
    if (!node) {
       fprintf(stderr, "Failed to retrieve output node. Invalid index specified?\n");
       vseval_freeScript(se);
       vseval_finalize();
       return 1;
    }

	bool error = false;
	const VSVideoInfo *vi = vsapi->getVideoInfo(node);

	if (showInfo) {
		fprintf(outFile, "Width: %d\n", vi->width);
		fprintf(outFile, "Height: %d\n", vi->height);
		fprintf(outFile, "Frames: %d\n", vi->numFrames);
		fprintf(outFile, "FPS: %" PRId64 "/%" PRId64 "\n", vi->fpsNum, vi->fpsDen);

		if (vi->format) {
			fprintf(outFile, "Format Name: %s\n", vi->format->name);
			fprintf(outFile, "Color Family: %s\n", colorFamilyToString(vi->format->colorFamily));
			fprintf(outFile, "Bits: %d\n", vi->format->bitsPerSample);
			fprintf(outFile, "SubSampling W: %d\n", vi->format->subSamplingW);
			fprintf(outFile, "SubSampling H: %d\n", vi->format->subSamplingH);
		} else {
			fprintf(outFile, "Format Name: Variable\n");
		}
	} else {
		if (!isConstantFormat(vi) || vi->numFrames == 0) {
			fprintf(stderr, "Cannot output clips with varying dimensions or unknown length\n");
			vsapi->freeNode(node);
			vseval_freeScript(se);
			vseval_finalize();
			return 1;
		}

		error = outputNode();
	}

	fflush(outFile);
    vsapi->freeNode(node);
    vseval_freeScript(se);
    vseval_finalize();

	return error;
}
Beispiel #11
0
static void VS_CC mvflowblurCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
    (void)userData;

    MVFlowBlurData d;
    MVFlowBlurData *data;

    int err;

    d.blur = (float)vsapi->propGetFloat(in, "blur", 0, &err);
    if (err)
        d.blur = 50.0f;

    d.prec = int64ToIntS(vsapi->propGetInt(in, "prec", 0, &err));
    if (err)
        d.prec = 1;

    d.thscd1 = int64ToIntS(vsapi->propGetInt(in, "thscd1", 0, &err));
    if (err)
        d.thscd1 = MV_DEFAULT_SCD1;

    d.thscd2 = int64ToIntS(vsapi->propGetInt(in, "thscd2", 0, &err));
    if (err)
        d.thscd2 = MV_DEFAULT_SCD2;

    d.opt = !!vsapi->propGetInt(in, "opt", 0, &err);
    if (err)
        d.opt = 1;


    if (d.blur < 0.0f || d.blur > 200.0f) {
        vsapi->setError(out, "FlowBlur: blur must be between 0 and 200 % (inclusive).");
        return;
    }

    if (d.prec < 1) {
        vsapi->setError(out, "FlowBlur: prec must be at least 1.");
        return;
    }

    d.blur256 = (int)(d.blur * 256.0f / 200.0f);


    d.super = vsapi->propGetNode(in, "super", 0, NULL);

#define ERROR_SIZE 1024
    char errorMsg[ERROR_SIZE] = "FlowBlur: failed to retrieve first frame from super clip. Error message: ";
    size_t errorLen = strlen(errorMsg);
    const VSFrameRef *evil = vsapi->getFrame(0, d.super, errorMsg + errorLen, ERROR_SIZE - errorLen);
#undef ERROR_SIZE
    if (!evil) {
        vsapi->setError(out, errorMsg);
        vsapi->freeNode(d.super);
        return;
    }
    const VSMap *props = vsapi->getFramePropsRO(evil);
    int evil_err[3];
    int nHeightS = int64ToIntS(vsapi->propGetInt(props, "Super_height", 0, &evil_err[0]));
    d.nSuperHPad = int64ToIntS(vsapi->propGetInt(props, "Super_hpad", 0, &evil_err[1]));
    int nSuperPel = int64ToIntS(vsapi->propGetInt(props, "Super_pel", 0, &evil_err[2]));
    vsapi->freeFrame(evil);

    for (int i = 0; i < 2; i++)
        if (evil_err[i]) {
            vsapi->setError(out, "FlowBlur: required properties not found in first frame of super clip. Maybe clip didn't come from mv.Super? Was the first frame trimmed away?");
            vsapi->freeNode(d.super);
            return;
        }


    d.mvbw = vsapi->propGetNode(in, "mvbw", 0, NULL);
    d.mvfw = vsapi->propGetNode(in, "mvfw", 0, NULL);

#define ERROR_SIZE 512
    char error[ERROR_SIZE + 1] = { 0 };
    const char *filter_name = "FlowBlur";

    adataFromVectorClip(&d.mvbw_data, d.mvbw, filter_name, "mvbw", vsapi, error, ERROR_SIZE);
    adataFromVectorClip(&d.mvfw_data, d.mvfw, filter_name, "mvfw", vsapi, error, ERROR_SIZE);

    scaleThSCD(&d.thscd1, &d.thscd2, &d.mvbw_data, filter_name, error, ERROR_SIZE);

    adataCheckSimilarity(&d.mvbw_data, &d.mvfw_data, filter_name, "mvbw", "mvfw", error, ERROR_SIZE);
#undef ERROR_SIZE

    if (error[0]) {
        vsapi->setError(out, error);

        vsapi->freeNode(d.super);
        vsapi->freeNode(d.mvfw);
        vsapi->freeNode(d.mvbw);
        return;
    }


    if (d.mvbw_data.nDeltaFrame <= 0 || d.mvfw_data.nDeltaFrame <= 0) {
        vsapi->setError(out, "FlowBlur: cannot use motion vectors with absolute frame references.");
        vsapi->freeNode(d.super);
        vsapi->freeNode(d.mvfw);
        vsapi->freeNode(d.mvbw);
        return;
    }

    // XXX Alternatively, use both clips' delta as offsets in GetFrame.
    if (d.mvfw_data.nDeltaFrame != d.mvbw_data.nDeltaFrame) {
        vsapi->setError(out, "FlowBlur: mvbw and mvfw must be generated with the same delta.");
        vsapi->freeNode(d.super);
        vsapi->freeNode(d.mvfw);
        vsapi->freeNode(d.mvbw);
        return;
    }

    // Make sure the motion vector clips are correct.
    if (!d.mvbw_data.isBackward || d.mvfw_data.isBackward) {
        if (!d.mvbw_data.isBackward)
            vsapi->setError(out, "FlowBlur: mvbw must be generated with isb=True.");
        else
            vsapi->setError(out, "FlowBlur: mvfw must be generated with isb=False.");
        vsapi->freeNode(d.super);
        vsapi->freeNode(d.mvfw);
        vsapi->freeNode(d.mvbw);
        return;
    }


    if (d.mvbw_data.nPel == 1)
        d.finest = vsapi->cloneNodeRef(d.super); // v2.0.9.1
    else {
        VSPlugin *mvtoolsPlugin = vsapi->getPluginById("com.nodame.mvtools", core);
        VSPlugin *stdPlugin = vsapi->getPluginById("com.vapoursynth.std", core);

        VSMap *args = vsapi->createMap();
        vsapi->propSetNode(args, "super", d.super, paReplace);
        vsapi->propSetInt(args, "opt", d.opt, paReplace);
        VSMap *ret = vsapi->invoke(mvtoolsPlugin, "Finest", args);
        if (vsapi->getError(ret)) {
#define ERROR_SIZE 512
            char error_msg[ERROR_SIZE + 1] = { 0 };
            snprintf(error_msg, ERROR_SIZE, "FlowBlur: %s", vsapi->getError(ret));
#undef ERROR_SIZE
            vsapi->setError(out, error_msg);

            vsapi->freeNode(d.super);
            vsapi->freeNode(d.mvfw);
            vsapi->freeNode(d.mvbw);
            vsapi->freeMap(args);
            vsapi->freeMap(ret);
            return;
        }
        d.finest = vsapi->propGetNode(ret, "clip", 0, NULL);
        vsapi->freeMap(ret);

        vsapi->clearMap(args);
        vsapi->propSetNode(args, "clip", d.finest, paReplace);
        vsapi->freeNode(d.finest);
        ret = vsapi->invoke(stdPlugin, "Cache", args);
        vsapi->freeMap(args);
        if (vsapi->getError(ret)) {
#define ERROR_SIZE 512
            char error_msg[ERROR_SIZE + 1] = { 0 };
            snprintf(error_msg, ERROR_SIZE, "FlowBlur: %s", vsapi->getError(ret));
#undef ERROR_SIZE
            vsapi->setError(out, error_msg);

            vsapi->freeNode(d.super);
            vsapi->freeNode(d.mvfw);
            vsapi->freeNode(d.mvbw);
            vsapi->freeMap(ret);
            return;
        }
        d.finest = vsapi->propGetNode(ret, "clip", 0, NULL);
        vsapi->freeMap(ret);
    }

    d.node = vsapi->propGetNode(in, "clip", 0, 0);
    d.vi = vsapi->getVideoInfo(d.node);

    const VSVideoInfo *supervi = vsapi->getVideoInfo(d.super);
    int nSuperWidth = supervi->width;

    if (d.mvbw_data.nHeight != nHeightS || d.mvbw_data.nWidth != nSuperWidth - d.nSuperHPad * 2 || d.mvbw_data.nPel != nSuperPel) {
        vsapi->setError(out, "FlowBlur: wrong source or super clip frame size.");
        vsapi->freeNode(d.finest);
        vsapi->freeNode(d.super);
        vsapi->freeNode(d.mvfw);
        vsapi->freeNode(d.mvbw);
        vsapi->freeNode(d.node);
        return;
    }

    if (!isConstantFormat(d.vi) || d.vi->format->bitsPerSample > 16 || d.vi->format->sampleType != stInteger || d.vi->format->subSamplingW > 1 || d.vi->format->subSamplingH > 1 || (d.vi->format->colorFamily != cmYUV && d.vi->format->colorFamily != cmGray)) {
        vsapi->setError(out, "FlowBlur: input clip must be GRAY, 420, 422, 440, or 444, up to 16 bits, with constant dimensions.");
        vsapi->freeNode(d.super);
        vsapi->freeNode(d.finest);
        vsapi->freeNode(d.mvfw);
        vsapi->freeNode(d.mvbw);
        vsapi->freeNode(d.node);
        return;
    }


    d.nHeightUV = d.mvbw_data.nHeight / d.mvbw_data.yRatioUV;
    d.nWidthUV = d.mvbw_data.nWidth / d.mvbw_data.xRatioUV;
    d.nHPaddingUV = d.mvbw_data.nHPadding / d.mvbw_data.xRatioUV;
    //d.nVPaddingUV = d.mvbw_data.nHPadding / d.mvbw_data.yRatioUV; // original looks wrong
    d.nVPaddingUV = d.mvbw_data.nVPadding / d.mvbw_data.yRatioUV;

    d.VPitchY = d.mvbw_data.nWidth;
    d.VPitchUV = d.nWidthUV;

    simpleInit(&d.upsizer, d.mvbw_data.nWidth, d.mvbw_data.nHeight, d.mvbw_data.nBlkX, d.mvbw_data.nBlkY, d.opt);
    if (d.vi->format->colorFamily != cmGray)
        simpleInit(&d.upsizerUV, d.nWidthUV, d.nHeightUV, d.mvbw_data.nBlkX, d.mvbw_data.nBlkY, d.opt);


    data = (MVFlowBlurData *)malloc(sizeof(d));
    *data = d;

    vsapi->createFilter(in, out, "FlowBlur", mvflowblurInit, mvflowblurGetFrame, mvflowblurFree, fmParallel, 0, data, core);
}
static void VS_CC mvmaskCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
    (void)userData;

    MVMaskData d;
    MVMaskData *data;

    int err;

    d.ml = (float)vsapi->propGetFloat(in, "ml", 0, &err);
    if (err)
        d.ml = 100.0f;

    d.fGamma = (float)vsapi->propGetFloat(in, "gamma", 0, &err);
    if (err)
        d.fGamma = 1.0f;

    d.kind = int64ToIntS(vsapi->propGetInt(in, "kind", 0, &err));

    double time = vsapi->propGetFloat(in, "time", 0, &err);
    if (err)
        time = 100.0;

    d.nSceneChangeValue = int64ToIntS(vsapi->propGetInt(in, "ysc", 0, &err));

    d.thscd1 = vsapi->propGetInt(in, "thscd1", 0, &err);
    if (err)
        d.thscd1 = MV_DEFAULT_SCD1;

    d.thscd2 = int64ToIntS(vsapi->propGetInt(in, "thscd2", 0, &err));
    if (err)
        d.thscd2 = MV_DEFAULT_SCD2;

    d.opt = !!vsapi->propGetInt(in, "opt", 0, &err);
    if (err)
        d.opt = 1;


    if (d.fGamma < 0.0f) {
        vsapi->setError(out, "Mask: gamma must not be negative.");
        return;
    }

    if (d.kind < 0 || d.kind > 5) {
        vsapi->setError(out, "Mask: kind must 0, 1, 2, 3, 4, or 5.");
        return;
    }

    if (time < 0.0 || time > 100.0) {
        vsapi->setError(out, "Mask: time must be between 0.0 and 100.0 (inclusive).");
        return;
    }

    if (d.nSceneChangeValue < 0 || d.nSceneChangeValue > 255) {
        vsapi->setError(out, "Mask: ysc must be between 0 and 255 (inclusive).");
        return;
    }


    d.vectors = vsapi->propGetNode(in, "vectors", 0, NULL);

#define ERROR_SIZE 512
    char error[ERROR_SIZE + 1] = { 0 };
    const char *filter_name = "Mask";

    adataFromVectorClip(&d.vectors_data, d.vectors, filter_name, "vectors", vsapi, error, ERROR_SIZE);

    scaleThSCD(&d.thscd1, &d.thscd2, &d.vectors_data, filter_name, error, ERROR_SIZE);
#undef ERROR_SIZE

    if (error[0]) {
        vsapi->setError(out, error);

        vsapi->freeNode(d.vectors);
        return;
    }


    d.fMaskNormFactor = 1.0f / d.ml; // Fizick
    d.fMaskNormFactor2 = d.fMaskNormFactor * d.fMaskNormFactor;

    d.fHalfGamma = d.fGamma * 0.5f;

    d.nWidthB = d.vectors_data.nBlkX * (d.vectors_data.nBlkSizeX - d.vectors_data.nOverlapX) + d.vectors_data.nOverlapX;
    d.nHeightB = d.vectors_data.nBlkY * (d.vectors_data.nBlkSizeY - d.vectors_data.nOverlapY) + d.vectors_data.nOverlapY;

    d.nHeightUV = d.vectors_data.nHeight / d.vectors_data.yRatioUV;
    d.nWidthUV = d.vectors_data.nWidth / d.vectors_data.xRatioUV;
    d.nHeightBUV = d.nHeightB / d.vectors_data.yRatioUV;
    d.nWidthBUV = d.nWidthB / d.vectors_data.xRatioUV;


    d.node = vsapi->propGetNode(in, "clip", 0, NULL);
    d.vi = *vsapi->getVideoInfo(d.node);

    if (!isConstantFormat(&d.vi) || d.vi.format->bitsPerSample > 8 || d.vi.format->subSamplingW > 1 || d.vi.format->subSamplingH > 1 || (d.vi.format->colorFamily != cmYUV && d.vi.format->colorFamily != cmGray)) {
        vsapi->setError(out, "Mask: input clip must be GRAY8, YUV420P8, YUV422P8, YUV440P8, or YUV444P8, with constant dimensions.");
        vsapi->freeNode(d.node);
        vsapi->freeNode(d.vectors);
        return;
    }

    if (d.vi.format->colorFamily == cmGray)
        d.vi.format = vsapi->getFormatPreset(pfYUV444P8, core);

    simpleInit(&d.upsizer, d.nWidthB, d.nHeightB, d.vectors_data.nBlkX, d.vectors_data.nBlkY, d.opt);
    simpleInit(&d.upsizerUV, d.nWidthBUV, d.nHeightBUV, d.vectors_data.nBlkX, d.vectors_data.nBlkY, d.opt);

    d.time256 = (int)(time * 256 / 100);


    data = (MVMaskData *)malloc(sizeof(d));
    *data = d;

    vsapi->createFilter(in, out, "Mask", mvmaskInit, mvmaskGetFrame, mvmaskFree, fmParallel, 0, data, core);
}
Beispiel #13
0
static void VS_CC lut2Create(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
    std::unique_ptr<Lut2Data> d(new Lut2Data(vsapi));

    d->node[0] = vsapi->propGetNode(in, "clipa", 0, 0);
    d->node[1] = vsapi->propGetNode(in, "clipb", 0, 0);
    d->vi[0] = vsapi->getVideoInfo(d->node[0]);
    d->vi[1] = vsapi->getVideoInfo(d->node[1]);

    if (!isConstantFormat(d->vi[0]) || !isConstantFormat(d->vi[1]))
        RETERROR("Lut2: only clips with constant format and dimensions supported");

    if (isCompatFormat(d->vi[0]) || isCompatFormat(d->vi[1]))
        RETERROR("Lut2: compat formats are not supported");

    if (d->vi[0]->format->sampleType != stInteger || d->vi[1]->format->sampleType != stInteger
        || (d->vi[0]->format->bitsPerSample + d->vi[1]->format->bitsPerSample) > 20
        || d->vi[0]->format->subSamplingH != d->vi[1]->format->subSamplingH
        || d->vi[0]->format->subSamplingW != d->vi[1]->format->subSamplingW
        || d->vi[0]->width != d->vi[1]->width || d->vi[0]->height != d->vi[1]->height)
        RETERROR("Lut2: only clips with integer samples, same dimensions, same subsampling and up to a total of 20 indexing bits supported");

    int n = d->vi[0]->format->numPlanes;
    int num_planes = vsapi->propNumElements(in, "planes");

    for (int i = 0; i < 3; i++)
        d->process[i] = (num_planes <= 0);

    for (int i = 0; i < num_planes; i++) {
        int o = int64ToIntS(vsapi->propGetInt(in, "planes", i, 0));

        if (o < 0 || o >= n)
            RETERROR("Lut2: plane index out of range");

        if (d->process[o])
            RETERROR("Lut2: plane specified twice");

        d->process[o] = true;
    }

    int err;
    VSFuncRef *func = vsapi->propGetFunc(in, "function", 0, &err);
    int lut_elem = vsapi->propNumElements(in, "lut");
	int lutf_elem = vsapi->propNumElements(in, "lutf");
	bool floatout = !!vsapi->propGetInt(in, "floatout", 0, &err);

    int num_set = (lut_elem >= 0) + (lutf_elem >= 0) + !!func;

	if (!num_set) {
		vsapi->freeFunc(func);
		RETERROR("Lut2: none of lut, lutf and function are set");
	}

	if (num_set > 1) {
		vsapi->freeFunc(func);
		RETERROR("Lut2: more than one of lut, lutf and function are set");
	}

	if (lut_elem >= 0 && floatout) {
		vsapi->freeFunc(func);
		RETERROR("Lut2: lut set but float output specified");
	}

	if (lutf_elem >= 0 && !floatout) {
		vsapi->freeFunc(func);
		RETERROR("Lut2: lutf set but float output not specified");
	}

    n = 1 << (d->vi[0]->format->bitsPerSample + d->vi[1]->format->bitsPerSample);

	int lut_length = std::max(lut_elem, lutf_elem);

	if (lut_length >= 0 && lut_length != n) {
		vsapi->freeFunc(func);
		RETERROR(("Lut2: bad lut length. Expected " + std::to_string(n) + " elements, got " + std::to_string(lut_length) + " instead").c_str());
	}

    int bitsout = int64ToIntS(vsapi->propGetInt(in, "bits", 0, &err));
    if (err)
        bitsout = floatout ? sizeof(float) * 8 : d->vi[0]->format->bitsPerSample;
	if ((floatout && bitsout != 32) || (!floatout && (bitsout < 8 || bitsout > 16))) {
		vsapi->freeFunc(func);
		RETERROR("Lut2: only 8-16 bit integer and 32 bit float output supported");
	}

    d->vi_out = *d->vi[0];
    d->vi_out.format = vsapi->registerFormat(d->vi[0]->format->colorFamily, floatout ? stFloat : stInteger, bitsout, d->vi[0]->format->subSamplingW, d->vi[0]->format->subSamplingH, core);


    if (d->vi[0]->format->bytesPerSample == 1) {
        if (d->vi[1]->format->bytesPerSample == 1) {
            if (d->vi_out.format->bytesPerSample == 1 && d->vi_out.format->sampleType == stInteger)
                lut2CreateHelper<uint8_t, uint8_t, uint8_t>(in, out, func, d, core, vsapi);
            else if (d->vi_out.format->bytesPerSample == 2 && d->vi_out.format->sampleType == stInteger)
                lut2CreateHelper<uint8_t, uint8_t, uint16_t>(in, out, func, d, core, vsapi);
            else if (d->vi_out.format->bitsPerSample == 32 && d->vi_out.format->sampleType == stFloat)
                lut2CreateHelper<uint8_t, uint8_t, float>(in, out, func, d, core, vsapi);
        } else if (d->vi[1]->format->bytesPerSample == 2) {
            if (d->vi_out.format->bytesPerSample == 1 && d->vi_out.format->sampleType == stInteger)
                lut2CreateHelper<uint8_t, uint16_t, uint8_t>(in, out, func, d, core, vsapi);
            else if (d->vi_out.format->bytesPerSample == 2 && d->vi_out.format->sampleType == stInteger)
                lut2CreateHelper<uint8_t, uint16_t, uint16_t>(in, out, func, d, core, vsapi);
            else if (d->vi_out.format->bitsPerSample == 32 && d->vi_out.format->sampleType == stFloat)
                lut2CreateHelper<uint8_t, uint16_t, float>(in, out, func, d, core, vsapi);
        }
    } else if (d->vi[0]->format->bytesPerSample == 2) {
        if (d->vi[1]->format->bytesPerSample == 1) {
            if (d->vi_out.format->bytesPerSample == 1 && d->vi_out.format->sampleType == stInteger)
                lut2CreateHelper<uint16_t, uint8_t, uint8_t>(in, out, func, d, core, vsapi);
            else if (d->vi_out.format->bytesPerSample == 2 && d->vi_out.format->sampleType == stInteger)
                lut2CreateHelper<uint16_t, uint8_t, uint16_t>(in, out, func, d, core, vsapi);
            else if (d->vi_out.format->bitsPerSample == 32 && d->vi_out.format->sampleType == stFloat)
                lut2CreateHelper<uint16_t, uint8_t, float>(in, out, func, d, core, vsapi);
        } else if (d->vi[1]->format->bytesPerSample == 2) {
            if (d->vi_out.format->bytesPerSample == 1 && d->vi_out.format->sampleType == stInteger)
                lut2CreateHelper<uint16_t, uint16_t, uint8_t>(in, out, func, d, core, vsapi);
            else if (d->vi_out.format->bytesPerSample == 2 && d->vi_out.format->sampleType == stInteger)
                lut2CreateHelper<uint16_t, uint16_t, uint16_t>(in, out, func, d, core, vsapi);
            else if (d->vi_out.format->bitsPerSample == 32 && d->vi_out.format->sampleType == stFloat)
                lut2CreateHelper<uint16_t, uint16_t, float>(in, out, func, d, core, vsapi);
        }
    }
}
Beispiel #14
0
static void VS_CC mvsuperCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
    (void)userData;

    MVSuperData d;
    MVSuperData *data;

    int err;

    d.nHPad = int64ToIntS(vsapi->propGetInt(in, "hpad", 0, &err));
    if (err)
        d.nHPad = 8;

    d.nVPad = int64ToIntS(vsapi->propGetInt(in, "vpad", 0, &err));
    if (err)
        d.nVPad = 8;

    d.nPel = int64ToIntS(vsapi->propGetInt(in, "pel", 0, &err));
    if (err)
        d.nPel = 2;

    d.nLevels = int64ToIntS(vsapi->propGetInt(in, "levels", 0, &err));

    d.chroma = !!vsapi->propGetInt(in, "chroma", 0, &err);
    if (err)
        d.chroma = 1;

    d.sharp = int64ToIntS(vsapi->propGetInt(in, "sharp", 0, &err)); // pel2 interpolation type
    if (err)
        d.sharp = SharpWiener;

    d.rfilter = int64ToIntS(vsapi->propGetInt(in, "rfilter", 0, &err));
    if (err)
        d.rfilter = RfilterBilinear;

    d.opt = !!vsapi->propGetInt(in, "opt", 0, &err);
    if (err)
        d.opt = 1;


    if ((d.nPel != 1) && (d.nPel != 2) && (d.nPel != 4)) {
        vsapi->setError(out, "Super: pel must be 1, 2, or 4.");
        return;
    }

    if (d.sharp < SharpBilinear || d.sharp > SharpWiener) {
        vsapi->setError(out, "Super: sharp must be between 0 and 2 (inclusive).");
        return;
    }

    if (d.rfilter < RfilterSimple || d.rfilter > RfilterCubic) {
        vsapi->setError(out, "Super: rfilter must be between 0 and 4 (inclusive).");
        return;
    }


    d.node = vsapi->propGetNode(in, "clip", 0, 0);
    d.vi = *vsapi->getVideoInfo(d.node);

    d.nWidth = d.vi.width;
    d.nHeight = d.vi.height;

    if (!isConstantFormat(&d.vi) || d.vi.format->bitsPerSample > 16 || d.vi.format->sampleType != stInteger || d.vi.format->subSamplingW > 1 || d.vi.format->subSamplingH > 1 || (d.vi.format->colorFamily != cmYUV && d.vi.format->colorFamily != cmGray)) {
        vsapi->setError(out, "Super: input clip must be GRAY, 420, 422, 440, or 444, up to 16 bits, with constant dimensions.");
        vsapi->freeNode(d.node);
        return;
    }

    if (d.vi.format->colorFamily == cmGray)
        d.chroma = 0;

    d.nModeYUV = d.chroma ? YUVPLANES : YPLANE;


    d.xRatioUV = 1 << d.vi.format->subSamplingW;
    d.yRatioUV = 1 << d.vi.format->subSamplingH;

    int nLevelsMax = 0;
    while (PlaneHeightLuma(d.vi.height, nLevelsMax, d.yRatioUV, d.nVPad) >= d.yRatioUV * 2 &&
           PlaneWidthLuma(d.vi.width, nLevelsMax, d.xRatioUV, d.nHPad) >= d.xRatioUV * 2) // at last two pixels width and height of chroma
    {
        nLevelsMax++;
    }
    if (d.nLevels <= 0 || d.nLevels > nLevelsMax)
        d.nLevels = nLevelsMax;

    d.pelclip = vsapi->propGetNode(in, "pelclip", 0, &err);
    const VSVideoInfo *pelvi = d.pelclip ? vsapi->getVideoInfo(d.pelclip) : NULL;

    if (d.pelclip && (!isConstantFormat(pelvi) || pelvi->format != d.vi.format)) {
        vsapi->setError(out, "Super: pelclip must have the same format as the input clip, and it must have constant dimensions.");
        vsapi->freeNode(d.node);
        vsapi->freeNode(d.pelclip);
        return;
    }

    d.usePelClip = 0;
    if (d.pelclip && (d.nPel >= 2)) {
        if ((pelvi->width == d.vi.width * d.nPel) &&
            (pelvi->height == d.vi.height * d.nPel)) {
            d.usePelClip = 1;
            d.isPelClipPadded = 0;
        } else if ((pelvi->width == (d.vi.width + d.nHPad * 2) * d.nPel) &&
                   (pelvi->height == (d.vi.height + d.nVPad * 2) * d.nPel)) {
            d.usePelClip = 1;
            d.isPelClipPadded = 1;
        } else {
            vsapi->setError(out, "Super: pelclip's dimensions must be multiples of the input clip's dimensions.");
            vsapi->freeNode(d.pelclip);
            vsapi->freeNode(d.node);
            return;
        }
    }

    d.nSuperWidth = d.nWidth + 2 * d.nHPad;
    d.nSuperHeight = PlaneSuperOffset(0, d.nHeight, d.nLevels, d.nPel, d.nVPad, d.nSuperWidth, d.yRatioUV) / d.nSuperWidth;
    if (d.yRatioUV == 2 && d.nSuperHeight & 1)
        d.nSuperHeight++; // even
    if (d.xRatioUV == 2 && d.nSuperWidth & 1)
        d.nSuperWidth++;
    d.vi.width = d.nSuperWidth;
    d.vi.height = d.nSuperHeight;


    data = (MVSuperData *)malloc(sizeof(d));
    *data = d;

    vsapi->createFilter(in, out, "Super", mvsuperInit, mvsuperGetFrame, mvsuperFree, fmParallel, 0, data, core);
}
Beispiel #15
0
int main(int argc, char **argv) {
    const VSAPI *vsapi = NULL;
    VSScript *se = NULL;
    FILE *outFile = NULL;

    if (argc != 3) {
        fprintf(stderr, "Usage: vsscript_example <infile> <outfile>\n");
        return 1;
    }

    // Open the output file for writing
    outFile = fopen(argv[2], "wb");

    if (!outFile) {
        fprintf(stderr, "Failed to open output for writing\n");
        return 1;
    }

    // Initialize VSScript, vsscript_finalize() needs to be called the same number of times as vsscript_init()
    if (!vsscript_init()) {
        // VapourSynth probably isn't properly installed at all
        fprintf(stderr, "Failed to initialize VapourSynth environment\n");
        return 1;
    }

    // Get a pointer to the normal api struct, exists so you don't have to link with the VapourSynth core library
    // Failure only happens on very rare API version mismatches
    vsapi = vsscript_getVSApi();
    assert(vsapi);

    // This line does the actual script evaluation. If se = NULL it will create a new environment
    if (vsscript_evaluateFile(&se, argv[1], 0)) {
        fprintf(stderr, "Script evaluation failed:\n%s", vsscript_getError(se));
        vsscript_freeScript(se);
        vsscript_finalize();
        return 1;
    }

    // Get the clip set as output. It is valid until the out index is re-set/cleared/the script is freed
    VSNodeRef *node = vsscript_getOutput(se, 0);
    if (!node) {
       fprintf(stderr, "Failed to retrieve output node\n");
       vsscript_freeScript(se);
       vsscript_finalize();
       return 1;
    }

    // Reject hard to handle formats
    const VSVideoInfo *vi = vsapi->getVideoInfo(node);

    if (!isConstantFormat(vi) || !vi->numFrames) {
        fprintf(stderr, "Cannot output clips with varying dimensions or unknown length\n");
        vsapi->freeNode(node);
        vsscript_freeScript(se);
        vsscript_finalize();
        return 1;
    }

    // Output all frames
    char errMsg[1024];
    int error = 0;
    for (int n = 0; n < vi->numFrames; n++) {
        const VSFrameRef *frame = vsapi->getFrame(n, node, errMsg, sizeof(errMsg));

        if (!frame) { // Check if an error happened when getting the frame
            error = 1;
            break;
        }

        // Loop over every row of every plane write to the file
        for (int p = 0; p < vi->format->numPlanes; p++) {
            int stride = vsapi->getStride(frame, p);
            const uint8_t *readPtr = vsapi->getReadPtr(frame, p);
            int rowSize = vsapi->getFrameWidth(frame, p) * vi->format->bytesPerSample;
            int height = vsapi->getFrameHeight(frame, p);

            for (int y = 0; y < height; y++) {
                // You should probably handle any fwrite errors here as well
                fwrite(readPtr, rowSize, 1, outFile);
                readPtr += stride;
            }
        }

        vsapi->freeFrame(frame);
    }

    // Cleanup
    fclose(outFile);

    vsapi->freeNode(node);
    vsscript_freeScript(se);
    vsscript_finalize();

    if (error) {
        fprintf(stderr, "%s", errMsg);
        return 1;
    }

    return 0;
}
Beispiel #16
0
int VBM3D_Data_Base::arguments_process(const VSMap *in, VSMap *out)
{
    int error;
    int m;

    // input - clip
    node = vsapi->propGetNode(in, "input", 0, nullptr);
    vi = vsapi->getVideoInfo(node);

    if (!isConstantFormat(vi))
    {
        setError(out, "Invalid input clip, only constant format input supported");
        return 1;
    }
    if ((vi->format->sampleType == stInteger && vi->format->bitsPerSample > 16)
        || (vi->format->sampleType == stFloat && vi->format->bitsPerSample != 32))
    {
        setError(out, "Invalid input clip, only 8-16 bit integer or 32 bit float formats supported");
        return 1;
    }

    // ref - clip
    rnode = vsapi->propGetNode(in, "ref", 0, &error);

    if (error)
    {
        rdef = false;
        rnode = node;
        rvi = vi;
    }
    else
    {
        rdef = true;
        rvi = vsapi->getVideoInfo(rnode);

        if (!isConstantFormat(rvi))
        {
            setError(out, "Invalid clip \"ref\", only constant format input supported");
            return 1;
        }
        if (rvi->format != vi->format)
        {
            setError(out, "input clip and clip \"ref\" must be of the same format");
            return 1;
        }
        if (rvi->width != vi->width || rvi->height != vi->height)
        {
            setError(out, "input clip and clip \"ref\" must be of the same width and height");
            return 1;
        }
        if (rvi->numFrames != vi->numFrames)
        {
            setError(out, "input clip and clip \"ref\" must have the same number of frames");
            return 1;
        }
    }

    // profile - data
    auto profile = vsapi->propGetData(in, "profile", 0, &error);

    if (error)
    {
        para.profile = para_default.profile;
    }
    else
    {
        para.profile = profile;
    }

    if (para.profile != "fast" && para.profile != "lc" && para.profile != "np"
        && para.profile != "high" && para.profile != "vn")
    {
        setError(out, "Unrecognized \"profile\" specified, should be \"fast\", \"lc\", \"np\", \"high\" or \"vn\"");
        return 1;
    }

    get_default_para(para.profile);

    // sigma - float[]
    m = vsapi->propNumElements(in, "sigma");

    if (m > 0)
    {
        int i;

        if (m > 3) m = 3;

        for (i = 0; i < m; ++i)
        {
            para.sigma[i] = vsapi->propGetFloat(in, "sigma", i, nullptr);

            if (para.sigma[i] < 0)
            {
                setError(out, "Invalid \"sigma\" assigned, must be a non-negative floating point number");
                return 1;
            }
        }

        for (; i < 3; ++i)
        {
            para.sigma[i] = para.sigma[i - 1];
        }
    }
    else
    {
        para.sigma = para_default.sigma;
    }

    // radius - int
    para.radius = int64ToIntS(vsapi->propGetInt(in, "radius", 0, &error));

    if (error)
    {
        para.radius = para_default.radius;
    }
    else if (para.radius < 1 || para.radius > 16)
    {
        setError(out, "Invalid \"radius\" assigned, must be an integer in [1, 16]");
        return 1;
    }

    // block_size - int
    para.BlockSize = int64ToIntS(vsapi->propGetInt(in, "block_size", 0, &error));

    if (error)
    {
        para.BlockSize = para_default.BlockSize;
    }
    else if (para.BlockSize < 1 || para.BlockSize > 64)
    {
        setError(out, "Invalid \"block_size\" assigned, must be an integer in [1, 64]");
        return 1;
    }
    else if (para.BlockSize > vi->width || para.BlockSize > vi->height)
    {
        setError(out, "Invalid \"block_size\" assigned, must not exceed width or height of the frame");
        return 1;
    }

    // block_step - int
    para.BlockStep = int64ToIntS(vsapi->propGetInt(in, "block_step", 0, &error));

    if (error)
    {
        para.BlockStep = para_default.BlockStep;
    }
    else if (para.BlockStep < 1 || para.BlockStep > para.BlockSize)
    {
        setError(out, "Invalid \"block_step\" assigned, must be an integer in [1, block_size]");
        return 1;
    }

    // group_size - int
    para.GroupSize = int64ToIntS(vsapi->propGetInt(in, "group_size", 0, &error));

    if (error)
    {
        para.GroupSize = para_default.GroupSize;
    }
    else if (para.GroupSize < 1 || para.GroupSize > 256)
    {
        setError(out, "Invalid \"group_size\" assigned, must be an integer in [1, 256]");
        return 1;
    }

    // bm_range - int
    para.BMrange = int64ToIntS(vsapi->propGetInt(in, "bm_range", 0, &error));

    if (error)
    {
        para.BMrange = para_default.BMrange;
    }
    else if (para.BMrange < 1)
    {
        setError(out, "Invalid \"bm_range\" assigned, must be a positive integer");
        return 1;
    }

    // bm_step - int
    para.BMstep = int64ToIntS(vsapi->propGetInt(in, "bm_step", 0, &error));

    if (error)
    {
        para.BMstep = para_default.BMstep;
    }
    else if (para.BMstep < 1 || para.BMstep > para.BMrange)
    {
        setError(out, "Invalid \"bm_step\" assigned, must be an integer in [1, bm_range]");
        return 1;
    }

    // ps_num - int
    para.PSnum = int64ToIntS(vsapi->propGetInt(in, "ps_num", 0, &error));

    if (error)
    {
        para.PSnum = para_default.PSnum;
    }
    else if (para.PSnum < 1 || para.PSnum > para.GroupSize)
    {
        setError(out, "Invalid \"ps_num\" assigned, must be an integer in [1, group_size]");
        return 1;
    }

    // ps_range - int
    para.PSrange = int64ToIntS(vsapi->propGetInt(in, "ps_range", 0, &error));

    if (error)
    {
        para.PSrange = para_default.PSrange;
    }
    else if (para.PSrange < 1)
    {
        setError(out, "Invalid \"ps_range\" assigned, must be a positive integer");
        return 1;
    }

    // ps_step - int
    para.PSstep = int64ToIntS(vsapi->propGetInt(in, "ps_step", 0, &error));

    if (error)
    {
        para.PSstep = para_default.PSstep;
    }
    else if (para.PSstep < 1 || para.PSstep > para.PSrange)
    {
        setError(out, "Invalid \"ps_step\" assigned, must be an integer in [1, ps_range]");
        return 1;
    }

    // th_mse - float
    para.thMSE = vsapi->propGetFloat(in, "th_mse", 0, &error);

    if (error)
    {
        para.thMSE_Default();
    }
    else if (para.thMSE <= 0)
    {
        setError(out, "Invalid \"th_mse\" assigned, must be a positive floating point number");
        return 1;
    }

    // matrix - int
    matrix = static_cast<ColorMatrix>(vsapi->propGetInt(in, "matrix", 0, &error));

    if (vi->format->colorFamily == cmRGB)
    {
        matrix = ColorMatrix::OPP;
    }
    else if (vi->format->colorFamily == cmYCoCg)
    {
        matrix = ColorMatrix::YCgCo;
    }
    else if (error || matrix == ColorMatrix::Unspecified)
    {
        matrix = ColorMatrix_Default(vi->width, vi->height);
    }
    else if (matrix != ColorMatrix::GBR && matrix != ColorMatrix::bt709
        && matrix != ColorMatrix::fcc && matrix != ColorMatrix::bt470bg && matrix != ColorMatrix::smpte170m
        && matrix != ColorMatrix::smpte240m && matrix != ColorMatrix::YCgCo && matrix != ColorMatrix::bt2020nc
        && matrix != ColorMatrix::bt2020c && matrix != ColorMatrix::OPP)
    {
        setError(out, "Unsupported \"matrix\" specified");
        return 1;
    }

    // process
    for (int i = 0; i < VSMaxPlaneCount; i++)
    {
        if (vi->format->colorFamily != cmRGB && para.sigma[i] == 0)
        {
            process[i] = 0;
        }
    }

    if (process[1] || process[2])
    {
        if (vi->format->subSamplingH || vi->format->subSamplingW)
        {
            setError(out, "input clip: sub-sampled format is not supported when chroma is processed, convert it to YUV444 or RGB first. "
                "For the best quality, RGB colorspace is recommended as input.");
            return 1;
        }
        if (rvi->format->subSamplingH || rvi->format->subSamplingW)
        {
            setError(out, "clip \"ref\": sub-sampled format is not supported when chroma is processed, convert it to YUV444 or RGB first. "
                "For the best quality, RGB colorspace is recommended as input.");
            return 1;
        }
    }

    return 0;
}
Beispiel #17
0
static void VS_CC vs_colorspace_create(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi)
{
	vs_colorspace_data *data = 0;
	zimg_colorspace_params params;
	zimg_filter *filter = 0;
	char fail_str[1024];

	VSNodeRef *node = 0;
	const VSVideoInfo *node_vi;
	const VSFormat *node_fmt;
	VSVideoInfo vi;

	node = vsapi->propGetNode(in, "clip", 0, 0);
	node_vi = vsapi->getVideoInfo(node);
	node_fmt = node_vi->format;

	if (!isConstantFormat(node_vi)) {
		strcpy(fail_str, "clip must have a defined format");
		goto fail;
	}
	if (node_fmt->numPlanes < 3 || node_fmt->subSamplingW || node_fmt->subSamplingH) {
		strcpy(fail_str, "colorspace conversion can only be performed on 4:4:4 clips");
		goto fail;
	}

	zimg2_colorspace_params_default(&params, ZIMG_API_VERSION);

	params.width = node_vi->width;
	params.height = node_vi->height;

	params.matrix_in = (int)vsapi->propGetInt(in, "matrix_in", 0, 0);
	params.transfer_in = (int)vsapi->propGetInt(in, "transfer_in", 0, 0);
	params.primaries_in = (int)vsapi->propGetInt(in, "primaries_in", 0, 0);

	params.matrix_out = (int)propGetIntDefault(vsapi, in, "matrix_out", 0, params.matrix_in);
	params.transfer_out = (int)propGetIntDefault(vsapi, in, "transfer_out", 0, params.transfer_in);
	params.primaries_out = (int)propGetIntDefault(vsapi, in, "primaries_out", 0, params.primaries_in);

	params.pixel_type = translate_pixel(node_fmt);
	params.depth = node_fmt->bitsPerSample;
	params.range_in = (int)!!propGetIntDefault(vsapi, in, "fullrange_in", 0, params.matrix_in == ZIMG_MATRIX_RGB ? ZIMG_RANGE_FULL : ZIMG_RANGE_LIMITED);
	params.range_out = (int)!!propGetIntDefault(vsapi, in, "fullrange_out", 0, params.matrix_out == ZIMG_MATRIX_RGB ? ZIMG_RANGE_FULL : ZIMG_RANGE_LIMITED);

	vi = *node_vi;
	vi.format = vsapi->registerFormat(params.matrix_out == ZIMG_MATRIX_RGB ? cmRGB : cmYUV,
	                                  node_fmt->sampleType, node_fmt->bitsPerSample, node_fmt->subSamplingW, node_fmt->subSamplingH, core);

	if (!(filter = zimg2_colorspace_create(&params))) {
		zimg_get_last_error(fail_str, sizeof(fail_str));
		goto fail;
	}
	if (!(data = malloc(sizeof(*data)))) {
		strcpy(fail_str, "error allocating vs_colorspace_data");
		goto fail;
	}

	data->filter = filter;
	data->node = node;
	data->vi = vi;
	data->matrix_out = params.matrix_out;
	data->transfer_out = params.transfer_out;
	data->primaries_out = params.primaries_out;

	vsapi->createFilter(in, out, "colorspace", vs_colorspace_init, vs_colorspace_get_frame, vs_colorspace_free, fmParallel, 0, data, core);
	return;
fail:
	vsapi->setError(out, fail_str);
	vsapi->freeNode(node);
	zimg2_filter_free(filter);
	free(data);
}
static void VS_CC exprCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
    JitExprData d;
    JitExprData *data;
    int err;

    try {

        for (int i = 0; i < 3; i++)
            d.node[i] = vsapi->propGetNode(in, "clips", i, &err);

        const VSVideoInfo *vi[3];
        for (int i = 0; i < 3; i++)
            if (d.node[i])
                vi[i] = vsapi->getVideoInfo(d.node[i]);
            else
                vi[i] = NULL;

        for (int i = 0; i < 3; i++) {
            if (vi[i]) {
                if (!isConstantFormat(vi[i]))
                    throw std::runtime_error("Only constant format input allowed");
                if (vi[0]->format->numPlanes != vi[i]->format->numPlanes
                    || vi[0]->format->subSamplingW != vi[i]->format->subSamplingW
                    || vi[0]->format->subSamplingH != vi[i]->format->subSamplingH
                    || vi[0]->width != vi[i]->width
                    || vi[0]->height != vi[i]->height)
                    throw std::runtime_error("All inputs must have the same number of planes and the same dimensions, subsampling included");
                if ((vi[i]->format->bitsPerSample > 16 && vi[i]->format->sampleType == stInteger)
                    || vi[i]->format->bitsPerSample != 32 && vi[i]->format->sampleType == stFloat)
                    throw std::runtime_error("Input clips must be 8-16 bit integer or 32 bit float format");
            }
        }

        d.vi = *vi[0];
        int format = int64ToIntS(vsapi->propGetInt(in, "format", 0, &err));
        if (!err) {
            const VSFormat *f = vsapi->getFormatPreset(format, core);
            if (f) {
                if (d.vi.format->colorFamily == cmCompat)
                    throw std::runtime_error("No compat formats allowed");
                if (d.vi.format->numPlanes != f->numPlanes)
                    throw std::runtime_error("The number of planes in the inputs and output must match");
                d.vi.format = vsapi->registerFormat(d.vi.format->colorFamily, f->sampleType, f->bitsPerSample, d.vi.format->subSamplingW, d.vi.format->subSamplingH, core);
            }
        }

        int nexpr = vsapi->propNumElements(in, "expr");
        if (nexpr > d.vi.format->numPlanes)
            throw std::runtime_error("More expressions given than there are planes");

        std::string expr[3];
        for (int i = 0; i < nexpr; i++)
            expr[i] = vsapi->propGetData(in, "expr", i, 0);
        if (nexpr == 1) {
            expr[1] = expr[0];
            expr[2] = expr[0];
        } else if (nexpr == 2) {
            expr[2] = expr[1];
        }

        for (int i = 0; i < 3; i++) {
            if (!expr[i].empty()) {
                d.plane[i] = poProcess;
            } else {
                if (d.vi.format->bitsPerSample == vi[0]->format->bitsPerSample && d.vi.format->sampleType == vi[0]->format->sampleType)
                    d.plane[i] = poCopy;
                else
                    d.plane[i] = poUndefined;
            }
        }

        const SOperation sop[3] = { getLoadOp(vi[0]), getLoadOp(vi[1]), getLoadOp(vi[2]) };
        int maxStackSize = 0;
        for (int i = 0; i < d.vi.format->numPlanes; i++)
            maxStackSize = std::max(parseExpression(expr[i], d.ops[i], sop, getStoreOp(&d.vi)), maxStackSize);

#ifdef VS_TARGET_CPU_X86
        d.stack = vs_aligned_malloc<void>(maxStackSize * 32, 32);
#else
        d.stack.resize(maxStackSize);
#endif
    } catch (std::runtime_error &e) {
        for (int i = 0; i < 3; i++)
            vsapi->freeNode(d.node[i]);
        std::string s = "Expr: ";
        s += e.what();
        vsapi->setError(out, s.c_str());
        return;
    }

    data = new JitExprData();
    *data = d;

    vsapi->createFilter(in, out, "Expr", exprInit, exprGetFrame, exprFree, fmParallelRequests, 0, data, core);
}
static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt )
{
	vs_hnd_t *h = new vs_hnd_t;
    if( !h )
        return -1;
	
	FILE *fh = x264_fopen(psz_filename, "rb");
	if (!fh)
		return -1;
	int b_regular = x264_is_regular_file(fh);
	fclose(fh);
	FAIL_IF_ERROR(!b_regular, "VS input is incompatible with non-regular file `%s'\n", psz_filename);

	FAIL_IF_ERROR(!vsscript_init(), "Failed to initialize VapourSynth environment\n");
	h->vsapi = vsscript_getVSApi();
	if (!h->vsapi) {
		fprintf(stderr, "Failed to get VapourSynth API pointer\n");
		vsscript_finalize();
		return -1;
	}

	// Should always succeed
	if (vsscript_createScript(&h->se)) {
		fprintf(stderr, "Script environment initialization failed:\n%s\n", vsscript_getError(h->se));
		vsscript_freeScript(h->se);
		vsscript_finalize();
		return -1;
	}

	std::string strfilename = psz_filename;
	nstring scriptFilename = s2ws(strfilename);
	if (vsscript_evaluateFile(&h->se, nstringToUtf8(scriptFilename).c_str(), efSetWorkingDir)) {
		fprintf(stderr, "Script evaluation failed:\n%s\n", vsscript_getError(h->se));
		vsscript_freeScript(h->se);
		vsscript_finalize();
		return -1;
	}

	h->node = vsscript_getOutput(h->se, 0);//outputIndex
	if (!h->node) {
		fprintf(stderr, "Failed to retrieve output node. Invalid index specified?\n");
		vsscript_freeScript(h->se);
		vsscript_finalize();
		return -1;
	}

	const VSCoreInfo *vsInfo = h->vsapi->getCoreInfo(vsscript_getCore(h->se));
    h->sea = new semaphore(vsInfo->numThreads);

	const VSVideoInfo *vi = h->vsapi->getVideoInfo(h->node);
	if (vi->format->colorFamily != cmYUV) {
		fprintf(stderr, "Can only read YUV format clips"); 
		h->vsapi->freeNode(h->node);
		vsscript_freeScript(h->se);
		vsscript_finalize();
        return -1;
    }
	
	if (!isConstantFormat(vi)) {
		fprintf(stderr, "Cannot output clips with varying dimensions\n");
		h->vsapi->freeNode(h->node);
		vsscript_freeScript(h->se);
		vsscript_finalize();
		return -1;
	}

	info->width = vi->width;
	info->height = vi->height;
	info->fps_num = vi->fpsNum;
	info->fps_den = vi->fpsDen;
	info->thread_safe = 1;
	info->num_frames = vi->numFrames;

	if (vi->format->subSamplingW == 1 && vi->format->subSamplingH == 1)
		info->csp = X264_CSP_I420;
    else if (vi->format->subSamplingW == 1 && vi->format->subSamplingH == 0)
        info->csp = X264_CSP_I422;
    else if (vi->format->subSamplingW == 0 && vi->format->subSamplingH == 0)
        info->csp = X264_CSP_I444;


	h->bit_depth = vi->format->bitsPerSample;
	if (h->bit_depth > 8)
	{
		info->csp |= X264_CSP_HIGH_DEPTH;
	}

	*p_handle = (void*)h;
    return 0;
}
Beispiel #20
0
static int reinit_vs(struct vf_instance *vf)
{
    struct vf_priv_s *p = vf->priv;
    VSMap *vars = NULL, *in = NULL, *out = NULL;
    int res = -1;

    destroy_vs(vf);

    MP_DBG(vf, "initializing...\n");

    // First load an empty script to get a VSScript, so that we get the vsapi
    // and vscore.
    if (vsscript_evaluateScript(&p->se, "", NULL, 0))
        goto error;
    p->vsapi = vsscript_getVSApi();
    p->vscore = vsscript_getCore(p->se);
    if (!p->vsapi || !p->vscore)
        goto error;

    in = p->vsapi->createMap();
    out = p->vsapi->createMap();
    vars = p->vsapi->createMap();
    if (!in || !out || !vars)
        goto error;

    p->vsapi->createFilter(in, out, "Input", infiltInit, infiltGetFrame,
                           infiltFree, fmSerial, 0, vf, p->vscore);
    int vserr;
    p->in_node = p->vsapi->propGetNode(out, "clip", 0, &vserr);
    if (!p->in_node)
        goto error;

    if (p->vsapi->propSetNode(vars, "video_in", p->in_node, 0))
        goto error;

    vsscript_setVariable(p->se, vars);

    if (vsscript_evaluateFile(&p->se, p->cfg_file, 0)) {
        MP_FATAL(vf, "Script evaluation failed:\n%s\n", vsscript_getError(p->se));
        goto error;
    }
    p->out_node = vsscript_getOutput(p->se, 0);
    if (!p->out_node)
        goto error;

    const VSVideoInfo *vi = p->vsapi->getVideoInfo(p->out_node);
    if (!isConstantFormat(vi)) {
        MP_FATAL(vf, "Video format is required to be constant.\n");
        goto error;
    }

    MP_DBG(vf, "initialized.\n");
    res = 0;
error:
    if (p->vsapi) {
        p->vsapi->freeMap(in);
        p->vsapi->freeMap(out);
        p->vsapi->freeMap(vars);
    }
    if (res < 0)
        destroy_vs(vf);
    return res;
}
Beispiel #21
0
static void VS_CC vszimg_create(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi)
{
	struct vszimg_data *data = NULL;

	const VSVideoInfo *node_vi;
	const VSFormat *node_fmt;
	int format_id;

	char err_msg[64];

#define FAIL_BAD_VALUE(name) \
  do { \
    sprintf(err_msg, "%s: bad value", (name)); \
    goto fail; \
  } while (0)

#define TRY_GET_ENUM(name, out, flag, table) \
  do { \
    int enum_tmp; \
    if (tryGetEnum(vsapi, in, (name), &enum_tmp, &(flag), (table), ARRAY_SIZE((table)))) \
      FAIL_BAD_VALUE(name); \
    if ((flag)) \
      (out) = enum_tmp; \
  } while (0)

#define TRY_GET_ENUM_STR(name, out, table) \
  do { \
    int enum_tmp; \
    vszimg_bool flag; \
    if (tryGetEnumStr(vsapi, in, (name), &enum_tmp, &flag, (table), ARRAY_SIZE((table)))) \
      FAIL_BAD_VALUE(name); \
    if ((flag)) \
      (out) = enum_tmp; \
  } while (0)

	if (!(data = malloc(sizeof(*data)))) {
		sprintf(err_msg, "error allocating vszimg_data");
		goto fail;
	}

	_vszimg_default_init(data);

	if (vszimg_mutex_init(&data->graph_mutex)) {
		sprintf(err_msg, "error initializing mutex");
		goto fail;
	}
	data->graph_mutex_initialized = VSZIMG_TRUE;

	data->node = vsapi->propGetNode(in, "clip", 0, NULL);
	node_vi = vsapi->getVideoInfo(data->node);
	node_fmt = node_vi->format;

	data->vi = *node_vi;

	zimg_graph_builder_params_default(&data->params, ZIMG_API_VERSION);

	if (propGetUintDef(vsapi, in, "width", &data->vi.width, node_vi->width))
		FAIL_BAD_VALUE("width");
	if (propGetUintDef(vsapi, in, "height", &data->vi.height, node_vi->height))
		FAIL_BAD_VALUE("height");

	if (propGetSintDef(vsapi, in, "format", &format_id, pfNone))
		FAIL_BAD_VALUE("format");
	data->vi.format = (format_id == pfNone) ? node_fmt : vsapi->getFormatPreset(format_id, core);

	TRY_GET_ENUM("matrix", data->matrix, data->have_matrix, g_matrix_table);
	TRY_GET_ENUM("transfer", data->transfer, data->have_transfer, g_transfer_table);
	TRY_GET_ENUM("primaries", data->primaries, data->have_primaries, g_primaries_table);
	TRY_GET_ENUM("range", data->range, data->have_range, g_range_table);
	TRY_GET_ENUM("chromaloc", data->chromaloc, data->have_chromaloc, g_chromaloc_table);

	TRY_GET_ENUM("matrix_in", data->matrix_in, data->have_matrix_in, g_matrix_table);
	TRY_GET_ENUM("transfer_in", data->transfer_in, data->have_transfer_in, g_transfer_table);
	TRY_GET_ENUM("primaries_in", data->primaries_in, data->have_primaries_in, g_primaries_table);
	TRY_GET_ENUM("range_in", data->range_in, data->have_range_in, g_range_table);
	TRY_GET_ENUM("chromaloc_in", data->chromaloc_in, data->have_chromaloc_in, g_chromaloc_table);

	TRY_GET_ENUM_STR("resample_filter", data->params.resample_filter, g_resample_filter_table);
	data->params.filter_param_a = propGetFloatDef(vsapi, in, "filter_param_a", data->params.filter_param_a);
	data->params.filter_param_b = propGetFloatDef(vsapi, in, "filter_param_b", data->params.filter_param_b);

	TRY_GET_ENUM_STR("resample_filter_uv", data->params.resample_filter_uv, g_resample_filter_table);
	data->params.filter_param_a_uv = propGetFloatDef(vsapi, in, "filter_param_a_uv", data->params.filter_param_a_uv);
	data->params.filter_param_b_uv = propGetFloatDef(vsapi, in, "filter_param_b_uv", data->params.filter_param_b_uv);

	TRY_GET_ENUM_STR("dither_type", data->params.dither_type, g_dither_type_table);
	TRY_GET_ENUM_STR("cpu_type", data->params.cpu_type, g_cpu_type_table);

#undef FAIL_BAD_VALUE
#undef TRY_GET_ENUM
#undef TRY_GET_ENUM_STR

	/* Basic compatibility check. */
	if (isConstantFormat(node_vi) && isConstantFormat(&data->vi)) {
		zimg_image_format src_format;
		zimg_image_format dst_format;

		zimg_image_format_default(&src_format, ZIMG_API_VERSION);
		zimg_image_format_default(&dst_format, ZIMG_API_VERSION);

		src_format.width = node_vi->width;
		src_format.height = node_vi->height;

		dst_format.width = data->vi.width;
		dst_format.height = data->vi.height;

		if (translate_vsformat(node_vi->format, &src_format, err_msg))
			goto fail;
		if (translate_vsformat(data->vi.format, &dst_format, err_msg))
			goto fail;
	}

	vsapi->createFilter(in, out, "format", vszimg_init, vszimg_get_frame, vszimg_free, fmParallel, 0, data, core);
	return;
fail:
	vsapi->setError(out, err_msg);
	_vszimg_destroy(data, vsapi);
	free(data);
}
static void VS_CC mvflowfpsCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
    MVFlowFPSData d;
    MVFlowFPSData *data;

    int err;

    d.num = vsapi->propGetInt(in, "num", 0, &err);
    if (err)
        d.num = 25;

    d.den = vsapi->propGetInt(in, "den", 0, &err);
    if (err)
        d.den = 1;

    d.maskmode = int64ToIntS(vsapi->propGetInt(in, "mask", 0, &err));
    if (err)
        d.maskmode = 2;

    d.ml = vsapi->propGetFloat(in, "ml", 0, &err);
    if (err)
        d.ml = 100.0;

    d.blend = !!vsapi->propGetInt(in, "blend", 0, &err);
    if (err)
        d.blend = 1;

    d.thscd1 = int64ToIntS(vsapi->propGetInt(in, "thscd1", 0, &err));
    if (err)
        d.thscd1 = MV_DEFAULT_SCD1;

    d.thscd2 = int64ToIntS(vsapi->propGetInt(in, "thscd2", 0, &err));
    if (err)
        d.thscd2 = MV_DEFAULT_SCD2;

    d.isse = !!vsapi->propGetInt(in, "isse", 0, &err);
    if (err)
        d.isse = 1;


    if (d.maskmode < 0 || d.maskmode > 2) {
        vsapi->setError(out, "FlowFPS: mask must be 0, 1, or 2.");
        return;
    }

    if (d.ml <= 0.0) {
        vsapi->setError(out, "FlowFPS: ml must be greater than 0.");
        return;
    }


    d.super = vsapi->propGetNode(in, "super", 0, NULL);

    char errorMsg[1024];
    const VSFrameRef *evil = vsapi->getFrame(0, d.super, errorMsg, 1024);
    if (!evil) {
        vsapi->setError(out, std::string("FlowFPS: failed to retrieve first frame from super clip. Error message: ").append(errorMsg).c_str());
        vsapi->freeNode(d.super);
        return;
    }
    const VSMap *props = vsapi->getFramePropsRO(evil);
    int evil_err[2];
    int nHeightS = int64ToIntS(vsapi->propGetInt(props, "Super_height", 0, &evil_err[0]));
    d.nSuperHPad = int64ToIntS(vsapi->propGetInt(props, "Super_hpad", 0, &evil_err[1]));
    vsapi->freeFrame(evil);

    for (int i = 0; i < 2; i++)
        if (evil_err[i]) {
            vsapi->setError(out, "FlowFPS: required properties not found in first frame of super clip. Maybe clip didn't come from mv.Super? Was the first frame trimmed away?");
            vsapi->freeNode(d.super);
            return;
        }


    d.mvbw = vsapi->propGetNode(in, "mvbw", 0, NULL);
    d.mvfw = vsapi->propGetNode(in, "mvfw", 0, NULL);

    // XXX F**k all this trying.
    try {
        d.mvClipB = new MVClipDicks(d.mvbw, d.thscd1, d.thscd2, vsapi);
    } catch (MVException &e) {
        vsapi->setError(out, std::string("FlowFPS: ").append(e.what()).c_str());
        vsapi->freeNode(d.super);
        vsapi->freeNode(d.mvbw);
        vsapi->freeNode(d.mvfw);
        return;
    }

    try {
        d.mvClipF = new MVClipDicks(d.mvfw, d.thscd1, d.thscd2, vsapi);
    } catch (MVException &e) {
        vsapi->setError(out, std::string("FlowFPS: ").append(e.what()).c_str());
        vsapi->freeNode(d.super);
        vsapi->freeNode(d.mvfw);
        vsapi->freeNode(d.mvbw);
        delete d.mvClipB;
        return;
    }

    // XXX Alternatively, use both clips' delta as offsets in GetFrame.
    if (d.mvClipF->GetDeltaFrame() != d.mvClipB->GetDeltaFrame()) {
        vsapi->setError(out, "FlowFPS: mvbw and mvfw must be generated with the same delta.");
        vsapi->freeNode(d.super);
        vsapi->freeNode(d.mvfw);
        vsapi->freeNode(d.mvbw);
        delete d.mvClipB;
        delete d.mvClipF;
        return;
    }

    // Make sure the motion vector clips are correct.
    if (!d.mvClipB->IsBackward() || d.mvClipF->IsBackward()) {
        if (!d.mvClipB->IsBackward())
            vsapi->setError(out, "FlowFPS: mvbw must be generated with isb=True.");
        else
            vsapi->setError(out, "FlowFPS: mvfw must be generated with isb=False.");
        vsapi->freeNode(d.super);
        vsapi->freeNode(d.mvfw);
        vsapi->freeNode(d.mvbw);
        delete d.mvClipB;
        delete d.mvClipF;
        return;
    }

    try {
        d.bleh = new MVFilter(d.mvfw, "FlowFPS", vsapi);
    } catch (MVException &e) {
        vsapi->setError(out, std::string("FlowFPS: ").append(e.what()).c_str());
        vsapi->freeNode(d.super);
        vsapi->freeNode(d.mvfw);
        vsapi->freeNode(d.mvbw);
        delete d.mvClipB;
        delete d.mvClipF;
        return;
    }

    try {
        // So it checks the similarity of mvfw and mvfw? ?????
        // Copied straight from 2.5.11.3...
        d.bleh->CheckSimilarity(d.mvClipF, "mvfw");
        d.bleh->CheckSimilarity(d.mvClipB, "mvbw");
    } catch (MVException &e) {
        vsapi->setError(out, std::string("FlowFPS: ").append(e.what()).c_str());
        delete d.bleh;
        delete d.mvClipB;
        delete d.mvClipF;
        vsapi->freeNode(d.super);
        vsapi->freeNode(d.mvfw);
        vsapi->freeNode(d.mvbw);
        return;
    }

    if (d.bleh->nPel == 1)
        d.finest = vsapi->cloneNodeRef(d.super); // v2.0.9.1
    else
    {
        VSPlugin *mvtoolsPlugin = vsapi->getPluginById("com.nodame.mvtools", core);
        VSPlugin *stdPlugin = vsapi->getPluginById("com.vapoursynth.std", core);

        VSMap *args = vsapi->createMap();
        vsapi->propSetNode(args, "super", d.super, paReplace);
        vsapi->propSetInt(args, "isse", d.isse, paReplace);
        VSMap *ret = vsapi->invoke(mvtoolsPlugin, "Finest", args);
        if (vsapi->getError(ret)) {
            vsapi->setError(out, std::string("FlowFPS: ").append(vsapi->getError(ret)).c_str());

            delete d.bleh;
            delete d.mvClipB;
            delete d.mvClipF;
            vsapi->freeNode(d.super);
            vsapi->freeNode(d.mvfw);
            vsapi->freeNode(d.mvbw);
            vsapi->freeMap(args);
            vsapi->freeMap(ret);
            return;
        }
        d.finest = vsapi->propGetNode(ret, "clip", 0, NULL);
        vsapi->freeMap(ret);

        vsapi->clearMap(args);
        vsapi->propSetNode(args, "clip", d.finest, paReplace);
        vsapi->freeNode(d.finest);
        ret = vsapi->invoke(stdPlugin, "Cache", args);
        vsapi->freeMap(args);
        if (vsapi->getError(ret)) {
            // prefix the error messages
            vsapi->setError(out, std::string("FlowFPS: ").append(vsapi->getError(ret)).c_str());

            delete d.bleh;
            delete d.mvClipB;
            delete d.mvClipF;
            vsapi->freeNode(d.super);
            vsapi->freeNode(d.mvfw);
            vsapi->freeNode(d.mvbw);
            vsapi->freeMap(ret);
            return;
        }
        d.finest = vsapi->propGetNode(ret, "clip", 0, NULL);
        vsapi->freeMap(ret);
    }

    d.node = vsapi->propGetNode(in, "clip", 0, 0);
    d.vi = *vsapi->getVideoInfo(d.node);


    if (d.vi.fpsNum == 0 || d.vi.fpsDen == 0) {
        vsapi->setError(out, "FlowFPS: The input clip must have a frame rate. Invoke AssumeFPS if necessary.");
        vsapi->freeNode(d.finest);
        vsapi->freeNode(d.super);
        vsapi->freeNode(d.mvfw);
        vsapi->freeNode(d.mvbw);
        vsapi->freeNode(d.node);
        delete d.bleh;
        delete d.mvClipB;
        delete d.mvClipF;
        return;
    }

    int64_t numeratorOld = d.vi.fpsNum;
    int64_t denominatorOld = d.vi.fpsDen;
    int64_t numerator, denominator;

    if (d.num != 0 && d.den != 0) {
        numerator = d.num;
        denominator = d.den;
    } else {
        numerator = numeratorOld * 2; // double fps by default
        denominator = denominatorOld;
    }

    //  safe for big numbers since v2.1
    d.fa = denominator * numeratorOld;
    d.fb = numerator * denominatorOld;
    int64_t fgcd = gcd(d.fa, d.fb); // general common divisor
    d.fa /= fgcd;
    d.fb /= fgcd;

    setFPS(&d.vi, numerator, denominator);

    if (d.vi.numFrames)
        d.vi.numFrames = (int)(1 + (d.vi.numFrames - 1) * d.fb / d.fa);


    if (d.bleh->nWidth != d.vi.width || d.bleh->nHeight != d.vi.height) {
        vsapi->setError(out, "FlowFPS: inconsistent source and vector frame size.");
        vsapi->freeNode(d.finest);
        vsapi->freeNode(d.super);
        vsapi->freeNode(d.mvfw);
        vsapi->freeNode(d.mvbw);
        vsapi->freeNode(d.node);
        delete d.bleh;
        delete d.mvClipB;
        delete d.mvClipF;
        return;
    }



    const VSVideoInfo *supervi = vsapi->getVideoInfo(d.super);
    int nSuperWidth = supervi->width;

    if (d.bleh->nHeight != nHeightS || d.bleh->nWidth != nSuperWidth - d.nSuperHPad * 2) {
        vsapi->setError(out, "FlowFPS: wrong source or super clip frame size.");
        vsapi->freeNode(d.finest);
        vsapi->freeNode(d.super);
        vsapi->freeNode(d.mvfw);
        vsapi->freeNode(d.mvbw);
        vsapi->freeNode(d.node);
        delete d.bleh;
        delete d.mvClipB;
        delete d.mvClipF;
        return;
    }

    if (!((d.bleh->nWidth + d.bleh->nHPadding*2) == supervi->width && (d.bleh->nHeight + d.bleh->nVPadding*2) <= supervi->height)) {
        vsapi->setError(out, "FlowFPS: inconsistent clips frame size! Incomprehensible error messages are the best, right?");
        vsapi->freeNode(d.finest);
        vsapi->freeNode(d.super);
        vsapi->freeNode(d.mvfw);
        vsapi->freeNode(d.mvbw);
        vsapi->freeNode(d.node);
        delete d.bleh;
        delete d.mvClipB;
        delete d.mvClipF;
        return;
    }

    if (!isConstantFormat(&d.vi) || d.vi.format->bitsPerSample > 16 || d.vi.format->sampleType != stInteger || d.vi.format->subSamplingW > 1 || d.vi.format->subSamplingH > 1 || (d.vi.format->colorFamily != cmYUV && d.vi.format->colorFamily != cmGray)) {
        vsapi->setError(out, "FlowFPS: input clip must be GRAY, 420, 422, 440, or 444, up to 16 bits, with constant dimensions.");
        vsapi->freeNode(d.super);
        vsapi->freeNode(d.finest);
        vsapi->freeNode(d.mvfw);
        vsapi->freeNode(d.mvbw);
        vsapi->freeNode(d.node);
        delete d.bleh;
        delete d.mvClipB;
        delete d.mvClipF;
        return;
    }

    if (d.vi.format->bitsPerSample > 8)
        d.isse = 0;


    d.nBlkXP = (d.bleh->nBlkX * (d.bleh->nBlkSizeX - d.bleh->nOverlapX) + d.bleh->nOverlapX < d.bleh->nWidth) ? d.bleh->nBlkX + 1 : d.bleh->nBlkX;
    d.nBlkYP = (d.bleh->nBlkY * (d.bleh->nBlkSizeY - d.bleh->nOverlapY) + d.bleh->nOverlapY < d.bleh->nHeight) ? d.bleh->nBlkY + 1 : d.bleh->nBlkY;
    d.nWidthP = d.nBlkXP * (d.bleh->nBlkSizeX - d.bleh->nOverlapX) + d.bleh->nOverlapX;
    d.nHeightP = d.nBlkYP * (d.bleh->nBlkSizeY - d.bleh->nOverlapY) + d.bleh->nOverlapY;

    d.nWidthPUV = d.nWidthP / d.bleh->xRatioUV;
    d.nHeightPUV = d.nHeightP / d.bleh->yRatioUV;
    d.nHeightUV = d.bleh->nHeight / d.bleh->yRatioUV;
    d.nWidthUV = d.bleh->nWidth / d.bleh->xRatioUV;

    d.nHPaddingUV = d.bleh->nHPadding / d.bleh->xRatioUV;
    d.nVPaddingUV = d.bleh->nVPadding / d.bleh->yRatioUV;

    d.VPitchY = (d.nWidthP + 15) & (~15);
    d.VPitchUV = (d.nWidthPUV + 15) & (~15);


    d.VXFullYB = new uint8_t [d.nHeightP * d.VPitchY];
    d.VYFullYB = new uint8_t [d.nHeightP * d.VPitchY];

    d.VXFullYF = new uint8_t [d.nHeightP * d.VPitchY];
    d.VYFullYF = new uint8_t [d.nHeightP * d.VPitchY];

    d.VXSmallYB = new uint8_t [d.nBlkXP * d.nBlkYP];
    d.VYSmallYB = new uint8_t [d.nBlkXP * d.nBlkYP];

    d.VXSmallYF = new uint8_t [d.nBlkXP * d.nBlkYP];
    d.VYSmallYF = new uint8_t [d.nBlkXP * d.nBlkYP];

    if (d.maskmode == 2) {
        d.VXFullYBB = new uint8_t [d.nHeightP * d.VPitchY];
        d.VYFullYBB = new uint8_t [d.nHeightP * d.VPitchY];

        d.VXFullYFF = new uint8_t [d.nHeightP * d.VPitchY];
        d.VYFullYFF = new uint8_t [d.nHeightP * d.VPitchY];

        d.VXSmallYBB = new uint8_t [d.nBlkXP * d.nBlkYP];
        d.VYSmallYBB = new uint8_t [d.nBlkXP * d.nBlkYP];

        d.VXSmallYFF = new uint8_t [d.nBlkXP * d.nBlkYP];
        d.VYSmallYFF = new uint8_t [d.nBlkXP * d.nBlkYP];
    }

    d.MaskSmallB = new uint8_t [d.nBlkXP * d.nBlkYP];
    d.MaskFullYB = new uint8_t [d.nHeightP * d.VPitchY];

    d.MaskSmallF = new uint8_t [d.nBlkXP * d.nBlkYP];
    d.MaskFullYF = new uint8_t [d.nHeightP * d.VPitchY];

    d.upsizer = new SimpleResize(d.nWidthP, d.nHeightP, d.nBlkXP, d.nBlkYP);

    if (d.vi.format->colorFamily != cmGray) {
        d.VXFullUVB = new uint8_t [d.nHeightPUV * d.VPitchUV];
        d.VYFullUVB = new uint8_t [d.nHeightPUV * d.VPitchUV];
        d.VXFullUVF = new uint8_t [d.nHeightPUV * d.VPitchUV];
        d.VYFullUVF = new uint8_t [d.nHeightPUV * d.VPitchUV];
        d.VXSmallUVB = new uint8_t [d.nBlkXP * d.nBlkYP];
        d.VYSmallUVB = new uint8_t [d.nBlkXP * d.nBlkYP];
        d.VXSmallUVF = new uint8_t [d.nBlkXP * d.nBlkYP];
        d.VYSmallUVF = new uint8_t [d.nBlkXP * d.nBlkYP];

        if (d.maskmode == 2) {
            d.VXFullUVBB = new uint8_t [d.nHeightPUV * d.VPitchUV];
            d.VYFullUVBB = new uint8_t [d.nHeightPUV * d.VPitchUV];
            d.VXFullUVFF = new uint8_t [d.nHeightPUV * d.VPitchUV];
            d.VYFullUVFF = new uint8_t [d.nHeightPUV * d.VPitchUV];
            d.VXSmallUVBB = new uint8_t [d.nBlkXP * d.nBlkYP];
            d.VYSmallUVBB = new uint8_t [d.nBlkXP * d.nBlkYP];
            d.VXSmallUVFF = new uint8_t [d.nBlkXP * d.nBlkYP];
            d.VYSmallUVFF = new uint8_t [d.nBlkXP * d.nBlkYP];
        }

        d.MaskFullUVB = new uint8_t [d.nHeightPUV * d.VPitchUV];
        d.MaskFullUVF = new uint8_t [d.nHeightPUV * d.VPitchUV];

        d.upsizerUV = new SimpleResize(d.nWidthPUV, d.nHeightPUV, d.nBlkXP, d.nBlkYP);
    }



    d.LUTVB = new int[256];
    d.LUTVF = new int[256];

    d.nleftLast = -1000;
    d.nrightLast = -1000;


    data = (MVFlowFPSData *)malloc(sizeof(d));
    *data = d;

    // Can't use fmParallel because of nleftLast/nrightLast.
    vsapi->createFilter(in, out, "FlowFPS", mvflowfpsInit, mvflowfpsGetFrame, mvflowfpsFree, fmParallelRequests, 0, data, core);

    // AssumeFPS sets the _DurationNum and _DurationDen properties.
    VSNodeRef *node = vsapi->propGetNode(out, "clip", 0, NULL);
    VSMap *args = vsapi->createMap();
    vsapi->propSetNode(args, "clip", node, paReplace);
    vsapi->freeNode(node);
    vsapi->propSetInt(args, "fpsnum", d.vi.fpsNum, paReplace);
    vsapi->propSetInt(args, "fpsden", d.vi.fpsDen, paReplace);
    VSPlugin *stdPlugin = vsapi->getPluginById("com.vapoursynth.std", core);
    VSMap *ret = vsapi->invoke(stdPlugin, "AssumeFPS", args);
    const char *error = vsapi->getError(ret);
    if (error) {
        vsapi->setError(out, std::string("FlowFPS: Failed to invoke AssumeFPS. Error message: ").append(error).c_str());
        vsapi->freeMap(args);
        vsapi->freeMap(ret);
        return;
    }
    node = vsapi->propGetNode(ret, "clip", 0, NULL);
    vsapi->freeMap(ret);
    vsapi->clearMap(args);
    vsapi->propSetNode(args, "clip", node, paReplace);
    vsapi->freeNode(node);
    ret = vsapi->invoke(stdPlugin, "Cache", args);
    vsapi->freeMap(args);
    error = vsapi->getError(ret);
    if (error) {
        vsapi->setError(out, std::string("FlowFPS: Failed to invoke Cache. Error message: ").append(error).c_str());
        vsapi->freeMap(ret);
        return;
    }
    node = vsapi->propGetNode(ret, "clip", 0, NULL);
    vsapi->freeMap(ret);
    vsapi->propSetNode(out, "clip", node, paReplace);
    vsapi->freeNode(node);
}
Beispiel #23
0
int wmain(int argc, wchar_t **argv) {
    if (_setmode(_fileno(stdout), _O_BINARY) == -1)
        fprintf(stderr, "Failed to set stdout to binary mode\n");
    SetConsoleCtrlHandler(HandlerRoutine, TRUE);
#else
int main(int argc, char **argv) {
#endif

    if (argc == 2) {
        if (nstring(argv[1]) == NSTRING("-version")) {
            if (!vsscript_init()) {
                fprintf(stderr, "Failed to initialize VapourSynth environment\n");
                return 1;
            }

            vsapi = vsscript_getVSApi();
            if (!vsapi) {
                fprintf(stderr, "Failed to get VapourSynth API pointer\n");
                vsscript_finalize();
                return 1;
            }

            VSCore *core = vsapi->createCore(0);
            if (!core) {
                fprintf(stderr, "Failed to create core\n");
                vsscript_finalize();
                return 1;
            }

            const VSCoreInfo *info = vsapi->getCoreInfo(core);
            printf("%s", info->versionString);
            vsapi->freeCore(core);
            return 0;
        }
    }

    if (argc < 3) {
        fprintf(stderr, "VSPipe usage:\n");
        fprintf(stderr, "Show version info: vspipe -version\n");
        fprintf(stderr, "Show script info: vspipe script.vpy - -info\n");
        fprintf(stderr, "Write to stdout: vspipe script.vpy - [options]\n");
        fprintf(stderr, "Write to file: vspipe script.vpy <outFile> [options]\n");
        fprintf(stderr, "Available options:\n");
        fprintf(stderr, "Select output index: -index N\n");
        fprintf(stderr, "Set number of concurrent frame requests: -requests N\n");
        fprintf(stderr, "Add YUV4MPEG headers: -y4m\n");
        fprintf(stderr, "Print progress to stderr: -progress\n");
        fprintf(stderr, "Show video info: -info (overrides other options)\n");
        return 1;
    }

    nstring outputFilename = argv[2];
    if (outputFilename == NSTRING("-")) {
        outFile = stdout;
    } else {
#ifdef VS_TARGET_OS_WINDOWS
        outFile = _wfopen(outputFilename.c_str(), L"wb");
#else
        outFile = fopen(outputFilename.c_str(), "wb");
#endif
        if (!outFile) {
            fprintf(stderr, "Failed to open output for writing\n");
            return 1;
        }
    }

    for (int arg = 3; arg < argc; arg++) {
        nstring argString = argv[arg];
        if (argString == NSTRING("-y4m")) {
            y4m = true;
        } else if (argString == NSTRING("-info")) {
            showInfo = true;
        } else if (argString == NSTRING("-index")) {
            bool ok = false;
            if (argc <= arg + 1) {
                fprintf(stderr, "No index number specified\n");
                return 1;
            }

            if (!nstringToInt(argv[arg + 1], outputIndex)) {
                fprintf(stderr, "Couldn't convert %s to an integer\n", nstringToUtf8(argv[arg + 1]).c_str());
                return 1;
            }
            arg++;
        } else if (argString == NSTRING("-requests")) {
            bool ok = false;
            if (argc <= arg + 1) {
                fprintf(stderr, "No request number specified\n");
                return 1;
            }
            if (!nstringToInt(argv[arg + 1], requests)) {
                fprintf(stderr, "Couldn't convert %s to an integer\n", nstringToUtf8(argv[arg + 1]).c_str());
                return 1;
            }
            arg++;
        } else if (argString == NSTRING("-progress")) {
            printFrameNumber = true;
        } else {
            fprintf(stderr, "Unknown argument: %s\n", nstringToUtf8(argString).c_str());
            return 1;
        }
    }

    if (!vsscript_init()) {
        fprintf(stderr, "Failed to initialize VapourSynth environment\n");
        return 1;
    }

    vsapi = vsscript_getVSApi();
    if (!vsapi) {
        fprintf(stderr, "Failed to get VapourSynth API pointer\n");
        vsscript_finalize();
        return 1;
    }

    std::chrono::time_point<std::chrono::high_resolution_clock> start(std::chrono::high_resolution_clock::now());

    if (vsscript_evaluateFile(&se,  nstringToUtf8(argv[1]).c_str(), efSetWorkingDir)) {
        fprintf(stderr, "Script evaluation failed:\n%s\n", vsscript_getError(se));
        vsscript_freeScript(se);
        vsscript_finalize();
        return 1;
    }

    node = vsscript_getOutput(se, outputIndex);
    if (!node) {
       fprintf(stderr, "Failed to retrieve output node. Invalid index specified?\n");
       vsscript_freeScript(se);
       vsscript_finalize();
       return 1;
    }

    bool error = false;
    const VSVideoInfo *vi = vsapi->getVideoInfo(node);

    if (showInfo) {
        fprintf(outFile, "Width: %d\n", vi->width);
        fprintf(outFile, "Height: %d\n", vi->height);
        fprintf(outFile, "Frames: %d\n", vi->numFrames);
        fprintf(outFile, "FPS: %" PRId64 "/%" PRId64 "\n", vi->fpsNum, vi->fpsDen);

        if (vi->format) {
            fprintf(outFile, "Format Name: %s\n", vi->format->name);
            fprintf(outFile, "Color Family: %s\n", colorFamilyToString(vi->format->colorFamily));
            fprintf(outFile, "Bits: %d\n", vi->format->bitsPerSample);
            fprintf(outFile, "SubSampling W: %d\n", vi->format->subSamplingW);
            fprintf(outFile, "SubSampling H: %d\n", vi->format->subSamplingH);
        } else {
            fprintf(outFile, "Format Name: Variable\n");
        }
    } else {
        if (!isConstantFormat(vi) || vi->numFrames == 0) {
            fprintf(stderr, "Cannot output clips with varying dimensions or unknown length\n");
            vsapi->freeNode(node);
            vsscript_freeScript(se);
            vsscript_finalize();
            return 1;
        }

        lastFpsReportTime = std::chrono::high_resolution_clock::now();
        error = outputNode();
    }

    fflush(outFile);
    std::chrono::time_point<std::chrono::high_resolution_clock> end(std::chrono::high_resolution_clock::now());
    std::chrono::duration<double> elapsedSeconds = end - start;
    fprintf(stderr, "Output %d frames in %.2f seconds (%.2f fps)\n", outputFrames, elapsedSeconds.count(), outputFrames / elapsedSeconds.count());
    vsapi->freeNode(node);
    vsscript_freeScript(se);
    vsscript_finalize();

    return error;
}
Beispiel #24
0
int wmain(int argc, wchar_t **argv) {
    if (_setmode(_fileno(stdout), _O_BINARY) == -1)
        fprintf(stderr, "Failed to set stdout to binary mode\n");
    SetConsoleCtrlHandler(HandlerRoutine, TRUE);
#else
int main(int argc, char **argv) {
#endif
    nstring outputFilename, scriptFilename;
    bool showHelp = false;
    std::map<std::string, std::string> scriptArgs;
    int startFrame = 0;

    for (int arg = 1; arg < argc; arg++) {
        nstring argString = argv[arg];
        if (argString == NSTRING("-v") || argString == NSTRING("--version")) {
            showVersion = true;
        } else if (argString == NSTRING("-y") || argString == NSTRING("--y4m")) {
            y4m = true;
        } else if (argString == NSTRING("-p") || argString == NSTRING("--progress")) {
            printFrameNumber = true;
        } else if (argString == NSTRING("-i") || argString == NSTRING("--info")) {
            showInfo = true;
        } else if (argString == NSTRING("-h") || argString == NSTRING("--help")) {
            showHelp = true;
        } else if (argString == NSTRING("-s") || argString == NSTRING("--start")) {
            if (argc <= arg + 1) {
                fprintf(stderr, "No start frame specified\n");
                return 1;
            }

            if (!nstringToInt(argv[arg + 1], startFrame)) {
                fprintf(stderr, "Couldn't convert %s to an integer (start)\n", nstringToUtf8(argv[arg + 1]).c_str());
                return 1;
            }

            if (startFrame < 0) {
                fprintf(stderr, "Negative start frame specified\n");
                return 1;
            }

            completedFrames = startFrame;
            outputFrames = startFrame;
            requestedFrames = startFrame;
            lastFpsReportFrame = startFrame;

            arg++;
        } else if (argString == NSTRING("-e") || argString == NSTRING("--end")) {
            if (argc <= arg + 1) {
                fprintf(stderr, "No end frame specified\n");
                return 1;
            }

            if (!nstringToInt(argv[arg + 1], totalFrames)) {
                fprintf(stderr, "Couldn't convert %s to an integer (end)\n", nstringToUtf8(argv[arg + 1]).c_str());
                return 1;
            }

            if (totalFrames < 0) {
                fprintf(stderr, "Negative end frame specified\n");
                return 1;
            }

            totalFrames++;
            arg++;
        } else if (argString == NSTRING("-o") || argString == NSTRING("--outputindex")) {
            if (argc <= arg + 1) {
                fprintf(stderr, "No output index specified\n");
                return 1;
            }

            if (!nstringToInt(argv[arg + 1], outputIndex)) {
                fprintf(stderr, "Couldn't convert %s to an integer (index)\n", nstringToUtf8(argv[arg + 1]).c_str());
                return 1;
            }
            arg++;
        } else if (argString == NSTRING("-r") || argString == NSTRING("--requests")) {
            if (argc <= arg + 1) {
                fprintf(stderr, "Number of requests not specified\n");
                return 1;
            }
            if (!nstringToInt(argv[arg + 1], requests)) {
                fprintf(stderr, "Couldn't convert %s to an integer (requests)\n", nstringToUtf8(argv[arg + 1]).c_str());
                return 1;
            }
            arg++;
        } else if (argString == NSTRING("-a") || argString == NSTRING("--arg")) {
            if (argc <= arg + 1) {
                fprintf(stderr, "No argument specified\n");
                return 1;
            }

            std::string aLine = nstringToUtf8(argv[arg + 1]).c_str();
            size_t equalsPos = aLine.find("=");
            if (equalsPos == std::string::npos) {
                fprintf(stderr, "No value specified for argument: %s\n", aLine.c_str());
                return 1;
            }

            scriptArgs[aLine.substr(0, equalsPos)] = aLine.substr(equalsPos + 1);

            arg++;
        } else if (scriptFilename.empty() && !argString.empty() && argString.substr(0, 1) != NSTRING("-")) {
            scriptFilename = argString;
        } else if (outputFilename.empty() && !argString.empty() && (argString == NSTRING("-") || (argString.substr(0, 1) != NSTRING("-")))) {
            outputFilename = argString;
        } else {
            fprintf(stderr, "Unknown argument: %s\n", nstringToUtf8(argString).c_str());
            return 1;
        }
    }

    if (showVersion && argc > 2) {
        fprintf(stderr, "Cannot combine version information with other options\n");
        return 1;
    } else if (showVersion) {
        return printVersion() ? 0 : 1;
    } else if (showHelp || argc <= 1) {
        printHelp();
        return 1;
    } else if (scriptFilename.empty()) {
        fprintf(stderr, "No script file specified\n");
        return 1;
    } else if (outputFilename.empty()) {
        fprintf(stderr, "No output file specified\n");
        return 1;
    }

    if (outputFilename == NSTRING("-")) {
        outFile = stdout;
    } else {
#ifdef VS_TARGET_OS_WINDOWS
        outFile = _wfopen(outputFilename.c_str(), L"wb");
#else
        outFile = fopen(outputFilename.c_str(), "wb");
#endif
        if (!outFile) {
            fprintf(stderr, "Failed to open output for writing\n");
            return 1;
        }
    }

    if (!vsscript_init()) {
        fprintf(stderr, "Failed to initialize VapourSynth environment\n");
        return 1;
    }

    vsapi = vsscript_getVSApi();
    if (!vsapi) {
        fprintf(stderr, "Failed to get VapourSynth API pointer\n");
        vsscript_finalize();
        return 1;
    }
    
    // Should always succeed
    if (vsscript_createScript(&se)) {
        fprintf(stderr, "Script environment initialization failed:\n%s\n", vsscript_getError(se));
        vsscript_freeScript(se);
        vsscript_finalize();
        return 1;
    }

    {
        VSMap *foldedArgs = vsapi->createMap();
        for (const auto &iter : scriptArgs)
            vsapi->propSetData(foldedArgs, iter.first.c_str(), iter.second.c_str(), static_cast<int>(iter.second.size()), paAppend);
        vsscript_setVariable(se, foldedArgs);
        vsapi->freeMap(foldedArgs);
    }

    start = std::chrono::high_resolution_clock::now();
    if (vsscript_evaluateFile(&se, nstringToUtf8(scriptFilename).c_str(), efSetWorkingDir)) {
        fprintf(stderr, "Script evaluation failed:\n%s\n", vsscript_getError(se));
        vsscript_freeScript(se);
        vsscript_finalize();
        return 1;
    }

    node = vsscript_getOutput(se, outputIndex);
    if (!node) {
       fprintf(stderr, "Failed to retrieve output node. Invalid index specified?\n");
       vsscript_freeScript(se);
       vsscript_finalize();
       return 1;
    }

    bool error = false;
    const VSVideoInfo *vi = vsapi->getVideoInfo(node);

    if (showInfo) {
        if (vi->width && vi->height) {
            fprintf(outFile, "Width: %d\n", vi->width);
            fprintf(outFile, "Height: %d\n", vi->height);
        } else {
            fprintf(outFile, "Width: Variable\n");
            fprintf(outFile, "Height: Variable\n");
        }
        if (vi->numFrames)
            fprintf(outFile, "Frames: %d\n", vi->numFrames);
        else
            fprintf(outFile, "Frames: Unknown\n");
        if (vi->fpsNum && vi->fpsDen)
            fprintf(outFile, "FPS: %" PRId64 "/%" PRId64 " (%.3f fps)\n", vi->fpsNum, vi->fpsDen, vi->fpsNum/(double)vi->fpsDen);
        else
            fprintf(outFile, "FPS: Variable\n");

        if (vi->format) {
            fprintf(outFile, "Format Name: %s\n", vi->format->name);
            fprintf(outFile, "Color Family: %s\n", colorFamilyToString(vi->format->colorFamily));
            fprintf(outFile, "Bits: %d\n", vi->format->bitsPerSample);
            fprintf(outFile, "SubSampling W: %d\n", vi->format->subSamplingW);
            fprintf(outFile, "SubSampling H: %d\n", vi->format->subSamplingH);
        } else {
            fprintf(outFile, "Format Name: Variable\n");
        }
    } else {
        const VSVideoInfo *vi = vsapi->getVideoInfo(node);
        if (totalFrames == -1)
            totalFrames = vi->numFrames;
        if ((vi->numFrames && vi->numFrames < totalFrames) || completedFrames >= totalFrames) {
            fprintf(stderr, "Invalid range of frames to output specified:\nfirst: %d\nlast: %d\nclip length: %d\nframes to output: %d\n", completedFrames, totalFrames, vi->numFrames, totalFrames - completedFrames);
            vsapi->freeNode(node);
            vsscript_freeScript(se);
            vsscript_finalize();
            return 1;
        }

        if (!isConstantFormat(vi) || !totalFrames) {
            fprintf(stderr, "Cannot output clips with varying dimensions or unknown length\n");
            vsapi->freeNode(node);
            vsscript_freeScript(se);
            vsscript_finalize();
            return 1;
        }

        lastFpsReportTime = std::chrono::high_resolution_clock::now();;
        error = outputNode();
    }

    fflush(outFile);

    if (!showInfo) {
        int totalFrames = outputFrames - startFrame;
        std::chrono::duration<double> elapsedSeconds = std::chrono::high_resolution_clock::now() - start;
        fprintf(stderr, "Output %d frames in %.2f seconds (%.2f fps)\n", totalFrames, elapsedSeconds.count(), totalFrames / elapsedSeconds.count());
    }
    vsapi->freeNode(node);
    vsscript_freeScript(se);
    vsscript_finalize();

    return error ? 1 : 0;
}