📄 timgfilteravisynth.cpp
字号:
/*
* Copyright (c) 2002-2006 Milan Cutka
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "stdafx.h"
#include "TimgFilterAvisynth.h"
#include "IffdshowBase.h"
#include "IffdshowDecVideo.h"
#include "Tconvert.h"
#include "ffdebug.h"
bool debugPrint=false;
int maxBufferAhead=0;
int maxBufferBack=0;
//========================== TimgFilterAvisynth::Tffdshow_source ===============================
AVS_Value AVSC_CC TimgFilterAvisynth::Tffdshow_source::Create(AVS_ScriptEnvironment *env, AVS_Value args, void *user_data)
{
Tinput* input=(Tinput*)user_data;
AVS_Value v;
AVS_FilterInfo *fi;
AVS_Clip *new_clip=input->env->avs_new_c_filter(*input->env,&fi,args,0);
Tffdshow_source *filter=new Tffdshow_source(input,(VideoInfo&)fi->vi);
fi->user_data=filter;
fi->get_frame=get_frame;
fi->get_parity=get_parity;
fi->set_cache_hints=set_cache_hints;
fi->free_filter=free_filter;
input->env->avs_set_to_clip(&v, new_clip);
input->env->avs_release_clip(new_clip);
return v;
}
void AVSC_CC TimgFilterAvisynth::Tffdshow_source::free_filter(AVS_FilterInfo *fi)
{
if (fi && fi->user_data)
delete (Tffdshow_source*)fi->user_data;
}
TimgFilterAvisynth::Tffdshow_source::Tffdshow_source(Tinput *Iinput,VideoInfo &Ivi):
input(Iinput),
vi(Ivi)
{
memset(&vi,0,sizeof(VideoInfo));
vi.width=input->dx;
vi.height=input->dy;
vi.fps_numerator=input->fpsnum;
vi.fps_denominator=input->fpsden;
vi.num_frames=NUM_FRAMES;
if (input->csp & FF_CSP_420P) vi.pixel_type=AVS_CS_YV12;
else if (input->csp & FF_CSP_YUY2) vi.pixel_type=AVS_CS_YUY2;
else if (input->csp & FF_CSP_RGB32) vi.pixel_type=AVS_CS_BGR32;
else if (input->csp & FF_CSP_RGB24) vi.pixel_type=AVS_CS_BGR24;
}
int TimgFilterAvisynth::findBuffer(TframeBuffer* buffers, int numBuffers, int n)
{
// Find the index of the buffer that's framenumber is closest (or equal to) n
int minDistance=MAX_INT;
int bestBufferNo=0;
if (buffers && numBuffers > 1)
for (int bufferNo=0; bufferNo < numBuffers; bufferNo++)
{
TframeBuffer& buffer=buffers[bufferNo];
if (buffer.frameNo < 0)
continue;
else if (buffer.frameNo == n)
{
bestBufferNo=bufferNo;
break;
}
int distance=abs(buffer.frameNo-n);
if (distance < minDistance)
{
minDistance=distance;
bestBufferNo=bufferNo;
}
}
return bestBufferNo;
}
AVS_VideoFrame* AVSC_CC TimgFilterAvisynth::Tffdshow_source::get_frame(AVS_FilterInfo *fi, int n)
{
Tffdshow_source *filter=(Tffdshow_source*)fi->user_data;
Tinput* input=filter->input;
PVideoFrame frame(input->env,input->env->avs_new_video_frame_a(*input->env,&filter->vi,16));
// Calculate request statistics for currently produced frame
int curFrameDistance=(n >= input->backLimit ? n : input->backLimit)-input->curFrame;
if (input->numAccessedFrames < 100)
input->accessedFrames[input->numAccessedFrames]=curFrameDistance;
if (input->minAccessedFrame > curFrameDistance)
input->minAccessedFrame=curFrameDistance;
if (input->maxAccessedFrame < curFrameDistance)
input->maxAccessedFrame=curFrameDistance;
input->numAccessedFrames++;
// Find the buffered frame that's closest to n and return it
if (input->numBuffers > 0)
{
TframeBuffer& buffer=input->buffers[findBuffer(input->buffers,input->numBuffers,n)];
if (debugPrint)
DPRINTF(_l("TimgFilterAvisynth: Looked up frame %i, using frame %i"),n,buffer.frameNo);
switch (filter->input->csp&FF_CSPS_MASK)
{
case FF_CSP_420P:
TffPict::copy(frame->GetWritePtr(PLANAR_Y),frame->GetPitch(PLANAR_Y),buffer.data[0],buffer.pitch[0],buffer.width[0],buffer.height[0]);
TffPict::copy(frame->GetWritePtr(PLANAR_U),frame->GetPitch(PLANAR_U),buffer.data[1],buffer.pitch[1],buffer.width[1],buffer.height[1]);
TffPict::copy(frame->GetWritePtr(PLANAR_V),frame->GetPitch(PLANAR_V),buffer.data[2],buffer.pitch[2],buffer.width[2],buffer.height[2]);
break;
case FF_CSP_YUY2:
TffPict::copy(frame->GetWritePtr(),frame->GetPitch(),buffer.data[0],buffer.pitch[0],buffer.width[0]*buffer.bytesPerPixel,buffer.height[0]);
break;
case FF_CSP_RGB24:
case FF_CSP_RGB32:
TffPict::copy(frame->GetWritePtr(),frame->GetPitch(),buffer.data[0]+buffer.pitch[0]*(buffer.height[0]-1),-buffer.pitch[0],buffer.width[0]*buffer.bytesPerPixel,buffer.height[0]);
break;
}
}
else
{
int count;
unsigned long* dest;
if (debugPrint)
DPRINTF(_l("TimgFilterAvisynth: Looked up frame %i, but no frames were buffered"),n);
switch (filter->input->csp&FF_CSPS_MASK)
{
case FF_CSP_420P:
memset(frame->GetWritePtr(PLANAR_Y),0,frame->GetPitch(PLANAR_Y)*frame->GetHeight(PLANAR_Y));
memset(frame->GetWritePtr(PLANAR_U),128,frame->GetPitch(PLANAR_U)*frame->GetHeight(PLANAR_U));
memset(frame->GetWritePtr(PLANAR_V),128,frame->GetPitch(PLANAR_V)*frame->GetHeight(PLANAR_V));
break;
case FF_CSP_YUY2:
for (dest=(unsigned long*)frame->GetWritePtr(),
count=frame->GetPitch()*frame->GetHeight();
count > 0;
dest++,
count-=sizeof(unsigned long))
*dest=0x80008000;
break;
case FF_CSP_RGB24:
case FF_CSP_RGB32:
memset(frame->GetWritePtr(),0,frame->GetPitch()*frame->GetHeight());
break;
}
}
return frame;
}
//============================= TimgFilterAvisynth::Tffdshow_setAR =============================
AVS_Value AVSC_CC TimgFilterAvisynth::Tffdshow_setAR::Create_SetSAR(AVS_ScriptEnvironment *env, AVS_Value args, void * user_data)
{
return Create(env,args,user_data,false);
}
AVS_Value AVSC_CC TimgFilterAvisynth::Tffdshow_setAR::Create_SetDAR(AVS_ScriptEnvironment *env, AVS_Value args, void * user_data)
{
return Create(env,args,user_data,true);
}
AVS_Value AVSC_CC TimgFilterAvisynth::Tffdshow_setAR::Create(AVS_ScriptEnvironment *env, AVS_Value args, void * user_data, bool setDAR)
{
Tinput* input=(Tinput*)user_data;
int x=avs_as_int(avs_array_elt(args,0));
int y=avs_as_int(avs_array_elt(args,1));
input->outputSar=( setDAR ? Rational(0,0) : Rational(x,y));
input->outputDar=(!setDAR ? Rational(0,0) : Rational(x,y));
return avs_void;
}
//================================ TimgFilterAvisynth::Tavisynth ===============================
bool TimgFilterAvisynth::Tavisynth::createClip(const TavisynthSettings *cfg,Tinput *input,TffPictBase& pict)
{
if (!input->env)
{
input->env=CreateScriptEnvironment(AVISYNTH_INTERFACE_VERSION);
if (!input->env)
return false;
input->env->AddFunction(
"ffdshow_source",
"",
TimgFilterAvisynth::Tffdshow_source::Create,
(void*)input);
input->env->AddFunction(
"ffdshow_setSAR",
"[x]i[y]i",
TimgFilterAvisynth::Tffdshow_setAR::Create_SetSAR,
(void*)input);
input->env->AddFunction(
"ffdshow_setDAR",
"[x]i[y]i",
TimgFilterAvisynth::Tffdshow_setAR::Create_SetDAR,
(void*)input);
}
IScriptEnvironment* env=input->env;
Rational sar=pict.rectClip.sar;
Rational dar=pict.rectClip.dar();
env->SetGlobalVar("ffdshow_sar_x",AVSValue(sar.num));
env->SetGlobalVar("ffdshow_sar_y",AVSValue(sar.den));
env->SetGlobalVar("ffdshow_dar_x",AVSValue(dar.num));
env->SetGlobalVar("ffdshow_dar_y",AVSValue(dar.den));
char script[2048];
// Convert script to ASCII; add ffdshow_source if the option for it is checked
_snprintf(
script,
sizeof(script)-1,
"%s%s%s",
cfg->ffdshowSource ? "ffdshow_source()\n" : "",
(const char*)text<char>(cfg->script),
cfg->ffdshowSource ? "\nreturn last" : "");
script[sizeof(script)-1]=0;
try
{
// Try using the script
AVSValue eval_args[]={script,"ffdshow_filter_avisynth_script"};
AVSValue val=env->Invoke("Eval",AVSValue(eval_args,2));
if (val.IsClip())
{
input->clip=new PClip(val,env);
return true;
}
else
throw AvisynthError("Invalid script!");
}
catch (AvisynthError &err)
{
// Create another script that contains only the source with the error subtitled onto it
char errMsg[1024];
_snprintf(errMsg,1023,"%s",(const char*)text<char>(err.msg)); errMsg[1023]=0;
for (char* errPtr=errMsg; *errPtr; errPtr++)
if (*errPtr == '\"')
*errPtr='\'';
else if (*errPtr == '\n')
*errPtr=' ';
_snprintf(script,2047,"return ffdshow_source().Subtitle(\"%s\")",errMsg);
try
{
// Try using that script
AVSValue err_eval_args[]={script,"ffdshow_filter_avisynth_error_script"};
AVSValue val=env->Invoke("Eval",AVSValue(err_eval_args,2));
if (val.IsClip())
{
input->clip=new PClip(val,env);
return true;
}
else
throw err;
}
catch (AvisynthError)
{
// Oh well...
throw err;
}
}
}
void TimgFilterAvisynth::Tavisynth::setOutFmt(const TavisynthSettings *cfg,Tinput *input,TffPictBase &pict)
{
if ((pict.rectClip == inputRect) && !(pict.rectClip.dar() != inputDar)) // No operator== in Rational... o_O;
{
if (outputRect != inputRect)
pict.rectFull=pict.rectClip=outputRect;
}
else if (createClip(cfg,input,pict))
{
IScriptEnvironment* env=input->env;
inputRect=pict.rectClip;
inputSar=pict.rectClip.sar;
inputDar=pict.rectClip.dar();
const VideoInfo &vi=(*input->clip)->GetVideoInfo();
if (input->outputDar)
pict.rectFull=pict.rectClip=outputRect=
Trect(
0,0,vi.width,vi.height,
Rational((vi.height*input->outputDar.num)/double(vi.width*input->outputDar.den),32768));
else if (input->outputSar)
pict.rectFull=pict.rectClip=outputRect=Trect(0,0,vi.width,vi.height,input->outputSar);
else if ((unsigned int)vi.width != inputRect.dx || (unsigned int)vi.height != inputRect.dy)
pict.rectFull=pict.rectClip=outputRect=Trect(0,0,vi.width,vi.height,inputSar);
else
outputRect=inputRect;
delete input->clip; input->clip=0;
}
}
void TimgFilterAvisynth::Tavisynth::skipAhead(bool passFirstThrough, bool clearLastOutStopTime)
{
// Skip ahead at least 1000 frames to make sure AviSynth doesn't use buffered frames,
// then round the new frame numbers up to make sure the relative alignment between the
// different framenumbers stays the same
// (Don't skip if buffering is turned off, though...)
int skippedFrames=(numBuffers > (applyPulldown == 1 ? 2 : 1) ? 1000 : 0);
REFERENCE_TIME roundToLong=frameScaleNum*frameScaleNum*frameScaleDen;
int roundTo=(int)(roundToLong > NUM_FRAMES ? NUM_FRAMES : roundToLong);
curInFrameNo=curInFrameNo+skippedFrames+(roundTo-1);
curInFrameNo-=curInFrameNo%roundTo;
if (curInFrameNo > TimgFilterAvisynth::NUM_FRAMES/2)
// Make sure we don't actually hit the end of the AviSynth clip
curInFrameNo=0;
backLimit=curInFrameNo;
curOutFrameNo=(int)(((REFERENCE_TIME)curInFrameNo)*frameScaleDen/frameScaleNum);
curOutScaledFrameNo=curInFrameNo;
if (clearLastOutStopTime)
lastOutStopTime=0;
resetBuffers=true;
this->passFirstThrough=passFirstThrough;
}
void TimgFilterAvisynth::Tavisynth::done(void)
{
if (bufferData) delete bufferData; bufferData=0;
if (buffers) delete buffers; buffers=0;
}
void TimgFilterAvisynth::Tavisynth::init(const TavisynthSettings &oldcfg, Tinput* input,int *outcsp, TffPictBase& pict)
{
infoBuf[0]=0;
if (createClip(&oldcfg,input,pict))
{
const VideoInfo &vi=(*input->clip)->GetVideoInfo();
if (vi.IsRGB24()) *outcsp=FF_CSP_RGB24|FF_CSP_FLAGS_VFLIP;
else if (vi.IsRGB32()) *outcsp=FF_CSP_RGB32|FF_CSP_FLAGS_VFLIP;
else if (vi.IsYUY2()) *outcsp=FF_CSP_YUY2;
else if (vi.IsYV12()) *outcsp=FF_CSP_420P;
else *outcsp=FF_CSP_NULL;
applyPulldown=oldcfg.applyPulldown;
enableBuffering=!!oldcfg.enableBuffering;
bufferAhead=(enableBuffering ? oldcfg.bufferAhead : 0);
bufferBack=(enableBuffering ? oldcfg.bufferBack : 0);
if (bufferAhead == 0 && bufferBack == 0)
enableBuffering=false;
buffersNeeded=bufferAhead+1;
input->numBuffers=numBuffers=
buffersNeeded+
bufferBack+
(enableBuffering && vi.num_frames != NUM_FRAMES ? 1 : 0)+
(applyPulldown == 1 ? 1 : 0);
curInFrameNo=0;
curOutFrameNo=0;
curOutScaledFrameNo=0;
backLimit=0;
lastOutStopTime=-1;
frameScaleNum=REFERENCE_TIME(NUM_FRAMES);
frameScaleDen=REFERENCE_TIME(vi.num_frames);
REFERENCE_TIME frameScaleGCD=lavc_gcd(frameScaleNum,frameScaleDen);
frameScaleNum/=frameScaleGCD;
frameScaleDen/=frameScaleGCD;
inputRect=pict.rectClip;
inputSar=pict.rectClip.sar;
inputDar=pict.rectClip.dar();
if (input->outputDar)
pict.rectFull=pict.rectClip=outputRect=
Trect(
0,0,vi.width,vi.height,
Rational((vi.height*input->outputDar.num)/double(vi.width*input->outputDar.den),32768));
else if (input->outputSar)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -