diff --git a/README b/README index 4ff3204..c4122c6 100644 --- a/README +++ b/README @@ -11,6 +11,7 @@ There's not much else to say, so let's get right to an example: core.std.LoadPlugin(path=r'C:\path\to\d2vsource.dll') ret = core.d2v.Source(input=r'C:\path\to\my.d2v') + ret = core.d2v.ApplyRFF(ret, d2v=r'C:\path\to\my.d2v') last = ret @@ -20,7 +21,32 @@ Parameters: Provides a speedup when you know you need to crop your image anyway, by avoiding extra memcpy calls. -There's not much else to it! + +About RFF Flags +--------------- + +Unlike DGDecode, it's up to the user to apply RFF flags as they see fit +using core.d2v.ApplyRFF(clip, d2v=r'C:\path\to\my.d2v') after calling +the source function. Unless you know your source is 100% FILM, you +probably want to apply these. DGDecode's traditional "Force FILM" mode +isn't really present in this plugin, but if your source or part of it +is 100% FILM, which is the only time you should be Force FILMing anyway, +you can simply set Force FILM in DGIndex, which will set the framerate +properly in the D2V file, and then not apply RFF flags. It's also +feasible to trim away any non-FILM frames and still Force FILM. + +A simple convenience function is: + + def D2VSource(d2vfile, cropping): + clip = core.d2v.Source(input=d2vfile, nocrop=cropping) + clip = core.d2v.ApplyRFF(clip, d2v=d2vfile) + return clip + +Parameters: + clip - Input clip. + d2v - D2V file for parsing RFF flags. This will be optional + in the future, once VapourSynth gets global metadata + support. Known Limitations & Bugs @@ -53,8 +79,8 @@ On Windows (Visual Studio): This assumes that you have the proper dependency paths in %INCLUDE% and %LIB%, and that you have built FFmpeg with Visual Studio as well. - cl /c /O2 /MT /EHsc /Icore /Ivs core/d2v.cpp core/compat.cpp core/decode.cpp vs/directrender.cpp vs/d2vsource.cpp - link /dll /out:d2vsource.dll directrender.obj d2vsource.obj d2v.obj compat.obj decode.obj libavutil.a libavformat.a libavcodec.a advapi32.lib ws2_32.lib + cl /c /O2 /MT /EHsc /Icore /Ivs core/d2v.cpp core/compat.cpp core/decode.cpp vs/directrender.cpp vs/applyrff.cpp vs/d2vsource.cpp + link /dll /out:d2vsource.dll directrender.obj applyrff.obj d2vsource.obj d2v.obj compat.obj decode.obj libavutil.a libavformat.a libavcodec.a advapi32.lib ws2_32.lib Please note that MinGW-build FFmpeg will be faster than one build with Visual Studio, due to its use of inline assembly. Also note that only MinGW-w64 is diff --git a/core/gop.hpp b/core/gop.hpp index fbdd4a3..96e97bb 100644 --- a/core/gop.hpp +++ b/core/gop.hpp @@ -31,6 +31,8 @@ extern "C" { #define GOP_FLAG_CLOSED 0x400 +#define FRAME_FLAG_RFF 0x01 + using namespace std; typedef struct frame { diff --git a/vs/applyrff.cpp b/vs/applyrff.cpp new file mode 100644 index 0000000..c7d8c3e --- /dev/null +++ b/vs/applyrff.cpp @@ -0,0 +1,222 @@ +/* + * VapourSynth D2V Plugin + * + * Copyright (c) 2012 Derek Buitenhuis + * + * This file is part of d2vsource. + * + * d2vsource is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * d2vsource is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with d2vsource; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +extern "C" { +#include +#include +} + +#include +#include + +#include "applyrff.hpp" +#include "d2v.hpp" +#include "gop.hpp" + +void VS_CC rffInit(VSMap *in, VSMap *out, void **instanceData, VSNode *node, VSCore *core, const VSAPI *vsapi) +{ + rffData *d = (rffData *) *instanceData; + vsapi->setVideoInfo(&d->vi, 1, node); +} + +const VSFrameRef *VS_CC rffGetFrame(int n, int activationReason, void **instanceData, void **frameData, + VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) +{ + rffData *d = (rffData *) *instanceData; + VSFrameRef *st, *sb, *f; + string msg; + int top, bottom; + int dst_stride[3], srct_stride[3], srcb_stride[3]; + 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(top, d->node, frameCtx); + vsapi->requestFrameFilter(bottom, d->node, frameCtx); + } + return NULL; + } + + /* Check if we're ready yet. */ + if (activationReason != arAllFramesReady) + return NULL; + + /* Source and destination frames. */ + st = (VSFrameRef *) vsapi->getFrameFilter(top, d->node, frameCtx); + sb = samefields ? NULL : (VSFrameRef *) vsapi->getFrameFilter(bottom, d->node, frameCtx); + f = vsapi->newVideoFrame(d->vi.format, d->vi.width, d->vi.height, NULL, core); + + /* Stash our strides for convenience. */ + for(i = 0; i < 3; i++) { + dst_stride[i] = vsapi->getStride(f, i); + srct_stride[i] = vsapi->getStride(st, i); + srcb_stride[i] = samefields ? 0 : vsapi->getStride(sb, i); + } + + /* Copy into VS's buffers. */ + if (samefields) { + /* Luma. */ + vs_bitblt(vsapi->getWritePtr(f, 0), dst_stride[0], vsapi->getWritePtr(st, 0), srct_stride[0], + d->vi.width, d->vi.height); + + /* Chroma. */ + vs_bitblt(vsapi->getWritePtr(f, 1), dst_stride[1], vsapi->getWritePtr(st, 1), srct_stride[1], + d->vi.width >> d->vi.format->subSamplingW, d->vi.height >> d->vi.format->subSamplingH); + vs_bitblt(vsapi->getWritePtr(f, 2), dst_stride[2], vsapi->getWritePtr(st, 2), srct_stride[2], + d->vi.width >> d->vi.format->subSamplingW, d->vi.height >> d->vi.format->subSamplingH); + } else { + /* Luma. */ + vs_bitblt(vsapi->getWritePtr(f, 0), dst_stride[0] * 2, + vsapi->getWritePtr(st, 0), srct_stride[0] * 2, + d->vi.width, d->vi.height / 2); + vs_bitblt(vsapi->getWritePtr(f, 0) + dst_stride[0], dst_stride[0] * 2, + vsapi->getWritePtr(sb, 0) + srcb_stride[0], srcb_stride[0] * 2, + d->vi.width, d->vi.height / 2); + + /* Chroma. */ + vs_bitblt(vsapi->getWritePtr(f, 1), dst_stride[1] * 2, + vsapi->getWritePtr(st, 1), srct_stride[1] * 2, + d->vi.width >> d->vi.format->subSamplingW, (d->vi.height >> d->vi.format->subSamplingH) / 2); + vs_bitblt(vsapi->getWritePtr(f, 1) + dst_stride[1], dst_stride[1] * 2, + vsapi->getWritePtr(sb, 1) + srcb_stride[1], srcb_stride[1] * 2, + d->vi.width >> d->vi.format->subSamplingW, (d->vi.height >> d->vi.format->subSamplingH) / 2); + + vs_bitblt(vsapi->getWritePtr(f, 2), dst_stride[2] * 2, + vsapi->getWritePtr(st, 2), srct_stride[2] * 2, + d->vi.width >> d->vi.format->subSamplingW, (d->vi.height >> d->vi.format->subSamplingH) / 2); + vs_bitblt(vsapi->getWritePtr(f, 2) + dst_stride[2], dst_stride[2] * 2, + vsapi->getWritePtr(sb, 2) + srcb_stride[2], srcb_stride[2] * 2, + d->vi.width >> d->vi.format->subSamplingW, (d->vi.height >> d->vi.format->subSamplingH) / 2); + } + + vsapi->freeFrame(st); + if (!samefields) + vsapi->freeFrame(sb); + + return f; +} + +void VS_CC rffFree(void *instanceData, VSCore *core, const VSAPI *vsapi) +{ + rffData *d = (rffData *) instanceData; + vsapi->freeNode(d->node); + d2vfreep(&d->d2v); + d->frames.clear(); + delete d; +} + +void VS_CC rffCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) +{ + rffData *data; + VSVideoInfo *vi; + fieldFrame ff = { -1, -1 }; + string msg; + int total_fields; + int i; + + /* Allocate our private data. */ + data = new rffData; + if (!data) { + vsapi->setError(out, "Cannot allocate private data."); + return; + } + + /* Parse the D2V to get flags. */ + data->d2v = d2vparse((char *) vsapi->propGetData(in, "d2v", 0, 0), msg); + if (!data->d2v) { + vsapi->setError(out, msg.c_str()); + return; + } + + /* Get our frame info and copy it, so we can modify it after. */ + data->node = vsapi->propGetNode(in, "clip", 0, 0); + vi = (VSVideoInfo *) vsapi->getVideoInfo(data->node); + data->vi = *vi; + + /* + * Parse all the RFF flags to figure out which fields go + * with which frames, and out total number of frames after + * apply the RFF flags. + */ + total_fields = 0; + data->frames.push_back(ff); + for(i = 0; i < data->vi.numFrames; i++) { + frame f = data->d2v->frames[i]; + int rff = data->d2v->gops[f.gop].flags[f.offset] & FRAME_FLAG_RFF; + int pos = data->frames.size() - 1; + + if (rff) { + if (data->frames[pos].top == -1) { + data->frames[pos].top = i; + data->frames[pos].bottom = i; + + ff.top = i; + ff.bottom = -1; + } else if (data->frames[pos].bottom == -1) { + data->frames[pos].bottom = i; + + ff.top = i; + ff.bottom = i; + } else { + ff.top = i; + ff.bottom = i; + + data->frames.push_back(ff); + + ff.bottom = -1; + } + } else { + if (data->frames[pos].top == -1) { + data->frames[pos].top = i; + data->frames[pos].bottom = i; + + ff.top = -1; + ff.bottom = -1; + } else if (data->frames[pos].bottom == -1) { + data->frames[pos].bottom = i; + + ff.top = i; + ff.bottom = -1; + } else { + ff.top = i; + ff.bottom = i; + } + } + data->frames.push_back(ff); + + total_fields += 2 + rff; + } + + data->vi.numFrames = total_fields / 2; + + vsapi->createFilter(in, out, "applyrff", rffInit, rffGetFrame, rffFree, fmSerial, 0, data, core); +} diff --git a/vs/applyrff.hpp b/vs/applyrff.hpp new file mode 100644 index 0000000..8092ba1 --- /dev/null +++ b/vs/applyrff.hpp @@ -0,0 +1,49 @@ +/* + * VapourSynth D2V Plugin + * + * Copyright (c) 2012 Derek Buitenhuis + * + * This file is part of d2vsource. + * + * d2vsource is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * d2vsource is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with d2vsource; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APPLYRFF_H +#define APPLYRFF_H + +#include +#include + +#include "d2v.hpp" + +typedef struct fieldFrame { + int top; + int bottom; +} fieldFrame; + +typedef struct rffData { + d2vcontext *d2v; + vector frames; + + VSVideoInfo vi; + VSNodeRef *node; +} rffData; + +void VS_CC rffInit(VSMap *in, VSMap *out, void **instanceData, VSNode *node, VSCore *core, const VSAPI *vsapi); +void VS_CC rffFree(void *instanceData, VSCore *core, const VSAPI *vsapi); +void VS_CC rffCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi); +const VSFrameRef *VS_CC rffGetFrame(int n, int activationReason, void **instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi); + +#endif diff --git a/vs/vapoursynth.cpp b/vs/vapoursynth.cpp index 8777f45..77afd91 100644 --- a/vs/vapoursynth.cpp +++ b/vs/vapoursynth.cpp @@ -28,10 +28,12 @@ extern "C" { #include #include +#include "applyrff.hpp" #include "d2vsource.hpp" VS_EXTERNAL_API(void) VapourSynthPluginInit(VSConfigPlugin configFunc, VSRegisterFunction registerFunc, VSPlugin *plugin) { configFunc("com.sources.d2vsource", "d2v", "D2V Source", VAPOURSYNTH_API_VERSION, 1, plugin); registerFunc("Source", "input:data;nocrop:int:opt;", d2vCreate, 0, plugin); + registerFunc("ApplyRFF", "clip:clip;d2v:data;", rffCreate, 0, plugin); }