Exemplo n.º 1
0
void VSVideoSource::OutputFrame(const FFMS_Frame *Frame, VSFrameRef *Dst, const VSAPI *vsapi) {
	const int RGBPlaneOrder[3] = {2, 0, 1};
	const VSFormat *fi = vsapi->getFrameFormat(Dst);
	if (fi->colorFamily == cmRGB) {
		for (int i = 0; i < fi->numPlanes; i++)
			vs_bitblt(vsapi->getWritePtr(Dst, i), vsapi->getStride(Dst, i), Frame->Data[RGBPlaneOrder[i]], Frame->Linesize[RGBPlaneOrder[i]],
			vsapi->getFrameWidth(Dst, i) * fi->bytesPerSample, vsapi->getFrameHeight(Dst, i));
	} else {
		for (int i = 0; i < fi->numPlanes; i++)
			vs_bitblt(vsapi->getWritePtr(Dst, i), vsapi->getStride(Dst, i), Frame->Data[i], Frame->Linesize[i],
			vsapi->getFrameWidth(Dst, i) * fi->bytesPerSample, vsapi->getFrameHeight(Dst, i));
	}
}
void processCL_sse2(const VSFrameRef * src, const VSFrameRef * scp, VSFrameRef * dst, VSFrameRef ** pad, const int field_n, const EEDI3CLData * d, const VSAPI * vsapi) {
    for (int plane = 0; plane < d->vi.format->numPlanes; plane++) {
        if (d->process[plane]) {
            copyPad<T>(src, pad[plane], plane, 1 - field_n, d->dh, vsapi);

            const int srcWidth = vsapi->getFrameWidth(pad[plane], 0);
            const int dstWidth = vsapi->getFrameWidth(dst, plane);
            const int srcHeight = vsapi->getFrameHeight(pad[plane], 0);
            const int dstHeight = vsapi->getFrameHeight(dst, plane);
            const int srcStride = vsapi->getStride(pad[plane], 0) / sizeof(T);
            const int dstStride = vsapi->getStride(dst, plane) / sizeof(T);
            const T * _srcp = reinterpret_cast<const T *>(vsapi->getReadPtr(pad[plane], 0));
            T * VS_RESTRICT _dstp = reinterpret_cast<T *>(vsapi->getWritePtr(dst, plane));

            const auto threadId = std::this_thread::get_id();
            auto queue = d->queue.at(threadId);
            auto calculateConnectionCosts = d->calculateConnectionCosts.at(threadId);
            auto srcImage = d->src.at(threadId);
            auto _ccosts = d->ccosts.at(threadId);
            float * pcosts = d->pcosts.at(threadId) + d->mdisVector;
            int * _pbackt = d->pbackt.at(threadId) + d->mdisVector;
            int * fpath = d->fpath.at(threadId);
            int * _dmap = d->dmap.at(threadId);
            float * tline = d->tline.at(threadId);

            const size_t globalWorkSize[] = { static_cast<size_t>((dstWidth + 15) & -16), static_cast<size_t>(d->vectorSize) };
            constexpr size_t localWorkSize[] = { 16, 4 };
            const int bufferSize = dstWidth * d->tpitchVector * sizeof(cl_float);

            vs_bitblt(_dstp + dstStride * (1 - field_n), vsapi->getStride(dst, plane) * 2,
                      _srcp + srcStride * (4 + 1 - field_n) + 12, vsapi->getStride(pad[plane], 0) * 2,
                      dstWidth * sizeof(T), dstHeight / 2);

            queue.enqueue_write_image(srcImage, compute::dim(0, 0), compute::dim(srcWidth, srcHeight), _srcp, vsapi->getStride(pad[plane], 0));

            for (int y = 4 + field_n; y < srcHeight - 4; y += 2 * d->vectorSize) {
                const int off = (y - 4 - field_n) >> 1;

                calculateConnectionCosts.set_args(srcImage, _ccosts, dstWidth, srcHeight - 4, y);
                queue.enqueue_nd_range_kernel(calculateConnectionCosts, 2, nullptr, globalWorkSize, localWorkSize);

                float * ccosts = reinterpret_cast<float *>(queue.enqueue_map_buffer(_ccosts, CL_MAP_READ, 0, bufferSize)) + d->mdisVector;

                // calculate path costs
                Vec4f().load(ccosts).store_a(pcosts);
                for (int x = 1; x < dstWidth; x++) {
                    const float * tT = ccosts + d->tpitchVector * x;
                    const float * ppT = pcosts + d->tpitchVector * (x - 1);
                    float * pT = pcosts + d->tpitchVector * x;
                    int * piT = _pbackt + d->tpitchVector * (x - 1);

                    const int umax = std::min({ x, dstWidth - 1 - x, d->mdis });
                    const int umax2 = std::min({ x - 1, dstWidth - x, d->mdis });

                    for (int u = -umax; u <= umax; u++) {
                        Vec4i idx = zero_128b();
                        Vec4f bval = FLT_MAX;

                        for (int v = std::max(-umax2, u - 1); v <= std::min(umax2, u + 1); v++) {
                            const Vec4f z = Vec4f().load_a(ppT + v * d->vectorSize) + d->gamma * std::abs(u - v);
                            const Vec4f ccost = min(z, FLT_MAX * 0.9f);
                            idx = select(Vec4ib(ccost < bval), v, idx);
                            bval = min(ccost, bval);
                        }

                        const Vec4f z = bval + Vec4f().load(tT + u * d->vectorSize);
                        min(z, FLT_MAX * 0.9f).store_a(pT + u * d->vectorSize);
                        idx.stream(piT + u * d->vectorSize);
                    }
                }

                for (int vs = 0; vs < d->vectorSize; vs++) {
                    const int realY = 4 + field_n + 2 * (off + vs);
                    if (realY >= srcHeight - 4)
                        break;

                    const T * srcp = _srcp + srcStride * realY + 12;
                    T * dstp = _dstp + dstStride * (field_n + 2 * (off + vs));
                    int * dmap = _dmap + dstWidth * (off + vs);

                    const T * src3p = srcp - srcStride * 3;
                    const T * src1p = srcp - srcStride;
                    const T * src1n = srcp + srcStride;
                    const T * src3n = srcp + srcStride * 3;

                    const int * pbackt = _pbackt + vs;

                    // backtrack
                    fpath[dstWidth - 1] = 0;
                    for (int x = dstWidth - 2; x >= 0; x--)
                        fpath[x] = pbackt[(d->tpitch * x + fpath[x + 1]) * d->vectorSize];

                    interpolate<T>(src3p, src1p, src1n, src3n, fpath, dmap, dstp, dstWidth, d->ucubic, d->peak);
                }

                queue.enqueue_unmap_buffer(_ccosts, ccosts - d->mdisVector);
            }

            if (d->vcheck) {
                const T * srcp = _srcp + srcStride * (4 + field_n) + 12;
                const T * scpp = nullptr;
                if (d->sclip)
                    scpp = reinterpret_cast<const T *>(vsapi->getReadPtr(scp, plane)) + dstStride * field_n;
                T * dstp = _dstp + dstStride * field_n;;

                vCheck<T>(srcp, scpp, dstp, _dmap, tline, field_n, dstWidth, srcHeight, srcStride, dstStride, d->vcheck, d->vthresh2, d->rcpVthresh0, d->rcpVthresh1, d->rcpVthresh2, d->peak);
            }
        }
    }
Exemplo n.º 3
0
void VSVideoSource::OutputAlphaFrame(const FFMS_Frame *Frame, int Plane, VSFrameRef *Dst, const VSAPI *vsapi) {
	const VSFormat *fi = vsapi->getFrameFormat(Dst);
	vs_bitblt(vsapi->getWritePtr(Dst, 0), vsapi->getStride(Dst, 0), Frame->Data[Plane], Frame->Linesize[Plane],
		vsapi->getFrameWidth(Dst, 0) * fi->bytesPerSample, vsapi->getFrameHeight(Dst, 0));
}
Exemplo n.º 4
0
const VSFrameRef *VS_CC d2vGetFrame(int n, int activationReason, void **instanceData, void **frameData,
                                    VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi)
{
    d2vData *d = (d2vData *) *instanceData;
    const VSFrameRef *s;
    VSFrameRef *f;
    VSMap *props;
    string msg;
    int ret;
    int plane;

    /* Unreference the previously decoded frame. */
    av_frame_unref(d->frame);

    ret = decodeframe(n, d->d2v, d->dec, d->frame, msg);
    if (ret < 0) {
        vsapi->setFilterError(msg.c_str(), frameCtx);
        return NULL;
    }

    /* Grab our direct-rendered frame. */
    s = (const VSFrameRef *) d->frame->opaque;
    if (!s) {
        vsapi->setFilterError("Seek pattern broke d2vsource! Please send a sample.", frameCtx);
        return NULL;
    }

    /* If our width and height are the same, just return it. */
    if (d->vi.width == d->aligned_width && d->vi.height == d->aligned_height) {
        f = vsapi->copyFrame(s, core);
    } else {
        f = vsapi->newVideoFrame(d->vi.format, d->vi.width, d->vi.height, NULL, core);

        /* Copy into VS's buffers. */
        for (plane = 0; plane < d->vi.format->numPlanes; plane++) {
            uint8_t *dstp = vsapi->getWritePtr(f, plane);
            const uint8_t *srcp = vsapi->getReadPtr(s, plane);
            int dst_stride = vsapi->getStride(f, plane);
            int src_stride = vsapi->getStride(s, plane);
            int width = vsapi->getFrameWidth(f, plane);
            int height = vsapi->getFrameHeight(f, plane);

            vs_bitblt(dstp, dst_stride, srcp, src_stride, width * d->vi.format->bytesPerSample, height);
        }
    }

    props = vsapi->getFramePropsRW(f);

    /*
     * The DGIndex manual simply says:
     *     "The matrix field displays the currently applicable matrix_coefficients value (colorimetry)."
     *
     * I can only assume this lines up with the tables VS uses correctly.
     */
    vsapi->propSetInt(props, "_Matrix", d->d2v->gops[d->d2v->frames[n].gop].matrix, paReplace);
    vsapi->propSetInt(props, "_DurationNum", d->d2v->fps_den, paReplace);
    vsapi->propSetInt(props, "_DurationDen", d->d2v->fps_num, paReplace);
    vsapi->propSetFloat(props, "_AbsoluteTime",
                        (static_cast<double>(d->d2v->fps_den) * n) / static_cast<double>(d->d2v->fps_num), paReplace);

    /*
     * YUVRGB_Scale describes the output range.
     * _ColorRange describes the input range.
     */
    if (d->d2v->yuvrgb_scale == PC)
        vsapi->propSetInt(props, "_ColorRange", 1, paReplace);
    else if (d->d2v->yuvrgb_scale == TV)
        vsapi->propSetInt(props, "_ColorRange", 0, paReplace);

    switch (d->frame->pict_type) {
    case AV_PICTURE_TYPE_I:
        vsapi->propSetData(props, "_PictType", "I", 1, paReplace);
        break;
    case AV_PICTURE_TYPE_P:
        vsapi->propSetData(props, "_PictType", "P", 1, paReplace);
        break;
    case AV_PICTURE_TYPE_B:
        vsapi->propSetData(props, "_PictType", "B", 1, paReplace);
        break;
    default:
        break;
    }

    int fieldbased;
    if (d->d2v->gops[d->d2v->frames[n].gop].flags[d->d2v->frames[n].offset] & FRAME_FLAG_PROGRESSIVE)
        fieldbased = 0;
    else
        fieldbased = 1 + !!(d->d2v->gops[d->d2v->frames[n].gop].flags[d->d2v->frames[n].offset] & FRAME_FLAG_TFF);
    vsapi->propSetInt(props, "_FieldBased", fieldbased, paReplace);

    vsapi->propSetInt(props, "_ChromaLocation", d->d2v->mpeg_type == 1 ? 1 : 0, paReplace);

    return f;
}
Exemplo n.º 5
0
const VSFrameRef *VS_CC rffGetFrame(int n, int activationReason, void **instanceData, void **frameData,
                                    VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi)
{
    const rffData *d = (const rffData *) *instanceData;
    const VSFrameRef *st, *sb;
    VSFrameRef *f;
    VSMap *props;
    int top, bottom;
    int fieldbased;
    int i;
    bool samefields;

    /* What frames to use for fields. */
    const rffField *top_field = &d->fields[n * 2];
    const rffField *bottom_field = &d->fields[n * 2 + 1];
    if (top_field->type == Bottom)
        std::swap(top_field, bottom_field);

    top    = top_field->frame;
    bottom = bottom_field->frame;

    samefields = top == bottom;

    /* Request out source frames. */
    if (activationReason == arInitial) {
        if (samefields) {
            vsapi->requestFrameFilter(top, d->node, frameCtx);
        } else {
            vsapi->requestFrameFilter(min(top, bottom), d->node, frameCtx);
            vsapi->requestFrameFilter(max(top, bottom), d->node, frameCtx);
        }
        return NULL;
    }

    /* Check if we're ready yet. */
    if (activationReason != arAllFramesReady)
        return NULL;

    /* Source and destination frames. */
    st = vsapi->getFrameFilter(top, d->node, frameCtx);
    sb = samefields ? NULL : vsapi->getFrameFilter(bottom, d->node, frameCtx);

    /* Copy into VS's buffers. */
    if (samefields) {
        f = vsapi->copyFrame(st, core);
    } else {
        int dst_stride[3], srct_stride[3], srcb_stride[3];

        /*
         * Copy properties from the first field's source frame.
         * Some of them will be wrong for this frame, but ¯\_(ツ)_/¯.
        */
        const VSFrameRef *prop_src = bottom_field < top_field ? sb : st;

        f  = vsapi->newVideoFrame(d->vi.format, d->vi.width, d->vi.height, prop_src, core);

        for (i = 0; i < d->vi.format->numPlanes; i++) {
            dst_stride[i]  = vsapi->getStride(f, i);
            srct_stride[i] = vsapi->getStride(st, i);
            srcb_stride[i] = vsapi->getStride(sb, i);

            uint8_t *dstp = vsapi->getWritePtr(f, i);
            const uint8_t *srctp = vsapi->getReadPtr(st, i);
            const uint8_t *srcbp = vsapi->getReadPtr(sb, i);
            int width = vsapi->getFrameWidth(f, i);
            int height = vsapi->getFrameHeight(f, i);

            vs_bitblt(dstp, dst_stride[i] * 2,
                      srctp, srct_stride[i] * 2,
                      width * d->vi.format->bytesPerSample, height / 2);

            vs_bitblt(dstp + dst_stride[i], dst_stride[i] * 2,
                      srcbp + srcb_stride[i], srcb_stride[i] * 2,
                      width * d->vi.format->bytesPerSample, height / 2);
        }
    }

    if (!samefields) {
        /* Set field order. */
        props = vsapi->getFramePropsRW(f);

        // They point to elements of an array, so pointer comparison is fine.
        if (bottom_field < top_field)
            fieldbased = 1; // bff
        else
            fieldbased = 2; // tff

        vsapi->propSetInt(props, "_FieldBased", fieldbased, paReplace);
    }

    vsapi->freeFrame(st);
    if (!samefields)
        vsapi->freeFrame(sb);

    return f;
}
Exemplo n.º 6
0
void mvpFillPlane(MVPlane *mvp, const uint8_t *pNewPlane, int nNewPitch) {
    if (!mvp->isFilled)
        vs_bitblt(mvp->pPlane[0] + mvp->nOffsetPadding, mvp->nPitch, pNewPlane, nNewPitch, mvp->nWidth * mvp->bytesPerSample, mvp->nHeight);
    mvp->isFilled = 1;
}
Exemplo n.º 7
0
static const VSFrameRef *VS_CC mvmaskGetFrame(int n, int activationReason, void **instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) {
    (void)frameData;

    MVMaskData *d = (MVMaskData *)*instanceData;

    if (activationReason == arInitial) {
        vsapi->requestFrameFilter(n, d->vectors, frameCtx);
        vsapi->requestFrameFilter(n, d->node, frameCtx);
    } else if (activationReason == arAllFramesReady) {
        const VSFrameRef *src = vsapi->getFrameFilter(n, d->node, frameCtx);
        VSFrameRef *dst = vsapi->newVideoFrame(d->vi.format, d->vi.width, d->vi.height, src, core);

        const uint8_t *pSrc[3];
        uint8_t *pDst[3];
        int nDstPitches[3];
        int nSrcPitches[3];

        pSrc[0] = vsapi->getReadPtr(src, 0);
        nSrcPitches[0] = vsapi->getStride(src, 0);

        for (int i = 0; i < 3; i++) {
            pDst[i] = vsapi->getWritePtr(dst, i);
            nDstPitches[i] = vsapi->getStride(dst, i);
        }

        FakeGroupOfPlanes fgop;
        const VSFrameRef *mvn = vsapi->getFrameFilter(n, d->vectors, frameCtx);
        fgopInit(&fgop, &d->vectors_data);
        const VSMap *mvprops = vsapi->getFramePropsRO(mvn);
        fgopUpdate(&fgop, (const uint8_t *)vsapi->propGetData(mvprops, prop_MVTools_vectors, 0, NULL));
        vsapi->freeFrame(mvn);

        const int kind = d->kind;
        const int nWidth = d->vectors_data.nWidth;
        const int nHeight = d->vectors_data.nHeight;
        const int nWidthUV = d->nWidthUV;
        const int nHeightUV = d->nHeightUV;
        const int nSceneChangeValue = d->nSceneChangeValue;

        if (fgopIsUsable(&fgop, d->thscd1, d->thscd2)) {
            const int nBlkX = d->vectors_data.nBlkX;
            const int nBlkY = d->vectors_data.nBlkY;
            const int nBlkCount = nBlkX * nBlkY;
            const float fMaskNormFactor = d->fMaskNormFactor;
            const float fMaskNormFactor2 = d->fMaskNormFactor2;
            const float fGamma = d->fGamma;
            const float fHalfGamma = d->fHalfGamma;
            const int nPel = d->vectors_data.nPel;
            const int nBlkSizeX = d->vectors_data.nBlkSizeX;
            const int nBlkSizeY = d->vectors_data.nBlkSizeY;
            const int nOverlapX = d->vectors_data.nOverlapX;
            const int nOverlapY = d->vectors_data.nOverlapY;
            const int nWidthB = d->nWidthB;
            const int nHeightB = d->nHeightB;
            const int nWidthBUV = d->nWidthBUV;
            const int nHeightBUV = d->nHeightBUV;
            SimpleResize *upsizer = &d->upsizer;
            SimpleResize *upsizerUV = &d->upsizerUV;
            const int time256 = d->time256;
            const int bitsPerSample = vsapi->getFrameFormat(src)->bitsPerSample;

            uint8_t *smallMask = (uint8_t *)malloc(nBlkX * nBlkY);
            uint8_t *smallMaskV = (uint8_t *)malloc(nBlkX * nBlkY);

            if (kind == 0) { // vector length mask
                for (int j = 0; j < nBlkCount; j++)
                    smallMask[j] = mvmaskLength(fgopGetBlock(&fgop, 0, j)->vector, nPel, fMaskNormFactor2, fHalfGamma);
            } else if (kind == 1) { // SAD mask
                MakeSADMaskTime(&fgop, nBlkX, nBlkY, 4.0 * fMaskNormFactor / (nBlkSizeX * nBlkSizeY), fGamma, nPel, smallMask, nBlkX, time256, nBlkSizeX - nOverlapX, nBlkSizeY - nOverlapY, bitsPerSample);
            } else if (kind == 2) { // occlusion mask
                MakeVectorOcclusionMaskTime(&fgop, d->vectors_data.isBackward, nBlkX, nBlkY, 1.0 / fMaskNormFactor, fGamma, nPel, smallMask, nBlkX, time256, nBlkSizeX - nOverlapX, nBlkSizeY - nOverlapY);
            } else if (kind == 3) { // vector x mask
                for (int j = 0; j < nBlkCount; j++)
                    smallMask[j] = VSMAX(0, VSMIN(255, (int)(fgopGetBlock(&fgop, 0, j)->vector.x * fMaskNormFactor * 100 + 128))); // shited by 128 for signed support
            } else if (kind == 4) { // vector y mask
                for (int j = 0; j < nBlkCount; j++)
                    smallMask[j] = VSMAX(0, VSMIN(255, (int)(fgopGetBlock(&fgop, 0, j)->vector.y * fMaskNormFactor * 100 + 128))); // shited by 128 for signed support
            } else if (kind == 5) {                                      // vector x mask in U, y mask in V
                for (int j = 0; j < nBlkCount; j++) {
                    VECTOR v = fgopGetBlock(&fgop, 0, j)->vector;
                    smallMask[j] = VSMAX(0, VSMIN(255, (int)(v.x * fMaskNormFactor * 100 + 128)));  // shited by 128 for signed support
                    smallMaskV[j] = VSMAX(0, VSMIN(255, (int)(v.y * fMaskNormFactor * 100 + 128))); // shited by 128 for signed support
                }
            }

            if (kind == 5) { // do not change luma for kind=5
                memcpy(pDst[0], pSrc[0], nSrcPitches[0] * nHeight);
            } else {
                upsizer->simpleResize_uint8_t(upsizer, pDst[0], nDstPitches[0], smallMask, nBlkX);
                if (nWidth > nWidthB)
                    for (int h = 0; h < nHeight; h++)
                        for (int w = nWidthB; w < nWidth; w++)
                            *(pDst[0] + h * nDstPitches[0] + w) = *(pDst[0] + h * nDstPitches[0] + nWidthB - 1);
                if (nHeight > nHeightB)
                    vs_bitblt(pDst[0] + nHeightB * nDstPitches[0], nDstPitches[0], pDst[0] + (nHeightB - 1) * nDstPitches[0], nDstPitches[0], nWidth, nHeight - nHeightB);
            }

            // chroma
            upsizerUV->simpleResize_uint8_t(upsizerUV, pDst[1], nDstPitches[1], smallMask, nBlkX);

            if (kind == 5)
                upsizerUV->simpleResize_uint8_t(upsizerUV, pDst[2], nDstPitches[2], smallMaskV, nBlkX);
            else
                memcpy(pDst[2], pDst[1], nHeightUV * nDstPitches[1]);

            if (nWidthUV > nWidthBUV)
                for (int h = 0; h < nHeightUV; h++)
                    for (int w = nWidthBUV; w < nWidthUV; w++) {
                        *(pDst[1] + h * nDstPitches[1] + w) = *(pDst[1] + h * nDstPitches[1] + nWidthBUV - 1);
                        *(pDst[2] + h * nDstPitches[2] + w) = *(pDst[2] + h * nDstPitches[2] + nWidthBUV - 1);
                    }
            if (nHeightUV > nHeightBUV) {
                vs_bitblt(pDst[1] + nHeightBUV * nDstPitches[1], nDstPitches[1], pDst[1] + (nHeightBUV - 1) * nDstPitches[1], nDstPitches[1], nWidthUV, nHeightUV - nHeightBUV);
                vs_bitblt(pDst[2] + nHeightBUV * nDstPitches[2], nDstPitches[2], pDst[2] + (nHeightBUV - 1) * nDstPitches[2], nDstPitches[2], nWidthUV, nHeightUV - nHeightBUV);
            }

            free(smallMask);
            free(smallMaskV);
        } else { // not usable
            if (kind == 5)
                memcpy(pDst[0], pSrc[0], nSrcPitches[0] * nHeight);
            else
                memset(pDst[0], nSceneChangeValue, nHeight * nDstPitches[0]);

            memset(pDst[1], nSceneChangeValue, nHeightUV * nDstPitches[1]);
            memset(pDst[2], nSceneChangeValue, nHeightUV * nDstPitches[2]);
        }

        fgopDeinit(&fgop);

        vsapi->freeFrame(src);

        return dst;
    }

    return 0;
}
Exemplo n.º 8
0
const VSFrameRef *VS_CC rffGetFrame(int n, int activationReason, void **instanceData, void **frameData,
                                    VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi)
{
    const rffData *d = (const rffData *) *instanceData;
    const VSFrameRef *st, *sb;
    VSFrameRef *f;
    VSMap *props;
    string msg;
    int top, bottom;
    int fieldbased;
    int i;
    bool samefields;

    /* What frames to use for fields. */
    top    = d->frames[n].top;
    bottom = d->frames[n].bottom;

    samefields = top == bottom;

    /* Request out source frames. */
    if (activationReason == arInitial) {
        if (samefields) {
            vsapi->requestFrameFilter(top, d->node, frameCtx);
        } else {
            vsapi->requestFrameFilter(min(top, bottom), d->node, frameCtx);
            vsapi->requestFrameFilter(max(top, bottom), d->node, frameCtx);
        }
        return NULL;
    }

    /* Check if we're ready yet. */
    if (activationReason != arAllFramesReady)
        return NULL;

    /* Source and destination frames. */
    st = vsapi->getFrameFilter(top, d->node, frameCtx);
    sb = samefields ? NULL : vsapi->getFrameFilter(bottom, d->node, frameCtx);

    /* Copy into VS's buffers. */
    if (samefields) {
        f = vsapi->copyFrame(st, core);
    } else {
        int dst_stride[3], srct_stride[3], srcb_stride[3];

        f  = vsapi->newVideoFrame(d->vi.format, d->vi.width, d->vi.height, NULL, core);

        for (i = 0; i < d->vi.format->numPlanes; i++) {
            dst_stride[i]  = vsapi->getStride(f, i);
            srct_stride[i] = vsapi->getStride(st, i);
            srcb_stride[i] = vsapi->getStride(sb, i);

            uint8_t *dstp = vsapi->getWritePtr(f, i);
            const uint8_t *srctp = vsapi->getReadPtr(st, i);
            const uint8_t *srcbp = vsapi->getReadPtr(sb, i);
            int width = vsapi->getFrameWidth(f, i);
            int height = vsapi->getFrameHeight(f, i);

            vs_bitblt(dstp, dst_stride[i] * 2,
                      srctp, srct_stride[i] * 2,
                      width * d->vi.format->bytesPerSample, height / 2);

            vs_bitblt(dstp + dst_stride[i], dst_stride[i] * 2,
                      srcbp + srcb_stride[i], srcb_stride[i] * 2,
                      width * d->vi.format->bytesPerSample, height / 2);
        }
    }

    /* Set field order. */
    props      = vsapi->getFramePropsRW(f);
    fieldbased = 1;
    if (samefields) {
        frame top_f  = d->d2v->frames[top];
        fieldbased  += !!(d->d2v->gops[top_f.gop].flags[top_f.offset] & FRAME_FLAG_TFF);
    } else {
        fieldbased += (top < bottom);
    }
    vsapi->propSetInt(props, "_FieldBased", fieldbased, paReplace);

    vsapi->freeFrame(st);
    if (!samefields)
        vsapi->freeFrame(sb);

    return f;
}