Improved handling of VFR files with DirectShow and Avisynth providers.

Originally committed to SVN as r667.
This commit is contained in:
Rodrigo Braz Monteiro 2007-01-01 03:29:20 +00:00
parent 1ea3e085d9
commit 5813033fa8
10 changed files with 236 additions and 57 deletions

View File

@ -243,20 +243,21 @@ void MatroskaWrapper::SetToTimecodes(FrameRate &target) {
// Check if it's CFR
bool isCFR = true;
double estimateCFR = timecodes.back() / timecodes.size()-1;
double curTime = 0;
for (int i=0;i<frames;i++) {
int delta = int(curTime - timecodes[i]);
if (abs(delta > 1)) {
double t1,t2;
for (int i=1;i<frames;i++) {
t1 = timecodes[i];
t2 = timecodes[i-1];
int delta = int(t1 - t2 - estimateCFR);
if (abs(delta > 2)) {
isCFR = false;
break;
}
curTime += estimateCFR;
}
// Constant framerate
if (isCFR) {
if (abs(estimateCFR - 23.976) < 0.01) estimateCFR = 23.976;
if (abs(estimateCFR - 29.97) < 0.01) estimateCFR = 29.97;
if (abs(estimateCFR - 23.976) < 0.01) estimateCFR = 24000.0 / 1001.0;
if (abs(estimateCFR - 29.97) < 0.01) estimateCFR = 30000.0 / 1001.0;
target.SetCFR(estimateCFR);
}

View File

@ -390,6 +390,114 @@ int FrameRate::GetTimeAtFrame(int frame,bool start,bool exact) {
}
////////////////////////////////////////
// Get the current list of frames/times
wxArrayInt FrameRate::GetFrameTimeList() {
wxArrayInt final;
for (unsigned int i=0;i<Frame.size();i++) final.Add(Frame[i]);
return final;
}
///////////////////////////////////////////
// Calculate the common FPS for evil stuff
// e.g., in a mix of 24fps and 30fps, returns 120fps
double FrameRate::GetCommonFPS() {
// Variables
int curDist;
int lastDist = 0;
int sectionStart = 0;
double curFps;
// List of likely frame rates
std::vector<double> frameRates;
frameRates.push_back(15.0 / 1.001);
frameRates.push_back(15);
frameRates.push_back(24.0 / 1.001);
frameRates.push_back(24);
frameRates.push_back(30.0 / 1.001);
frameRates.push_back(30);
frameRates.push_back(120.0 / 1.001);
frameRates.push_back(120);
// List of rates found
std::vector<double> found;
// Find the relative fps of each area
for (unsigned int i=1;i<Frame.size();i++) {
// Find the current frame distance
curDist = Frame[i]-Frame[i-1];
// See if it's close enough to the last
if ((abs(curDist - lastDist) < 2 || i-1 == sectionStart) && i != Frame.size()-1) {
lastDist = curDist;
continue;
}
// Calculate section fps
curFps = (i - sectionStart - 1) * 1000.0 / double(Frame[i-1]-Frame[sectionStart]);
sectionStart = i;
lastDist = curDist;
// See if it's close enough to one of the likely rates
for (unsigned int j=0;j<frameRates.size();j++) {
if (curFps-0.01 <= frameRates[j] && curFps+0.01 >= frameRates[j]) {
curFps = frameRates[j];
break;
}
}
// See if it's on list
bool onList = false;
for (unsigned int j=0;j<found.size();j++) {
if (found[j] == curFps) {
onList = true;
break;
}
}
// If not, add it
if (!onList) found.push_back(curFps);
}
// Find common between them
double v1,v2,minInt,tempd;
int tempi1,tempi2;
while (found.size() > 1) {
// Extract last two values
v1 = found.back();
found.pop_back();
v2 = found.back();
found.pop_back();
// Divide them
v2 = v1/v2;
// Find what it takes to make it an integer
for (minInt = 1;minInt<20;minInt++) {
tempd = v2 * minInt;
tempi1 = (int)(tempd-0.001);
tempi2 = (int)(tempd+0.001);
if (tempi1 != tempi2) break;
}
if (minInt != 20) v1 = v1*minInt;
// See if it's close enough to one of the likely rates
for (unsigned int j=0;j<frameRates.size();j++) {
if (v1-0.01 <= frameRates[j] && v1+0.01 >= frameRates[j]) {
v1 = frameRates[j];
break;
}
}
// Re-insert obtained result
found.push_back(v1);
}
return found.back();
}
///////////
// Globals
FrameRate VFR_Output;

View File

@ -77,6 +77,7 @@ private:
ASS_FrameRateType FrameRateType;
bool loaded;
wxString vfrFile;
public:
FrameRate();
~FrameRate();
@ -94,6 +95,9 @@ public:
bool IsLoaded() { return loaded; };
ASS_FrameRateType GetFrameRateType() { return FrameRateType; };
wxString GetFilename() { return vfrFile; };
wxArrayInt GetFrameTimeList();
double GetCommonFPS();
};

View File

@ -163,14 +163,9 @@ void VideoDisplay::SetVideo(const wxString &filename) {
if (!filename.IsEmpty()) {
try {
grid->CommitChanges(true);
bool isVfr = false;
double overFps = 0;
// Choose a provider
provider = VideoProvider::GetProvider(filename,GetTempWorkFile());
provider->SetZoom(zoomValue);
if (arType != 4) arValue = GetARFromType(arType); // 4 = custom
provider->SetDAR(arValue);
// Why the hell was this disabled?
// Read extra data from file
bool mkvOpen = MatroskaWrapper::wrapper.IsOpen();
wxString ext = filename.Right(4).Lower();
@ -186,7 +181,11 @@ void VideoDisplay::SetVideo(const wxString &filename) {
// Ask to override timecodes
int override = wxYES;
if (VFR_Output.IsLoaded()) override = wxMessageBox(_("You already have timecodes loaded. Replace them with the timecodes from the Matroska file?"),_("Replace timecodes?"),wxYES_NO | wxICON_QUESTION);
if (override == wxYES) MatroskaWrapper::wrapper.SetToTimecodes(VFR_Output);
if (override == wxYES) {
MatroskaWrapper::wrapper.SetToTimecodes(VFR_Output);
isVfr = VFR_Output.GetFrameRateType() == VFR;
if (isVfr) overFps = VFR_Output.GetCommonFPS();
}
// Close mkv
MatroskaWrapper::wrapper.Close();
@ -198,6 +197,13 @@ void VideoDisplay::SetVideo(const wxString &filename) {
}
#endif
// Choose a provider
provider = VideoProvider::GetProvider(filename,GetTempWorkFile(),overFps);
if (isVfr) provider->OverrideFrameTimeList(VFR_Output.GetFrameTimeList());
provider->SetZoom(zoomValue);
if (arType != 4) arValue = GetARFromType(arType); // 4 = custom
provider->SetDAR(arValue);
// Update size
UpdateSize();

View File

@ -45,7 +45,7 @@
////////////////
// Get provider
VideoProvider *VideoProvider::GetProvider(wxString video,wxString subtitles) {
VideoProvider *VideoProvider::GetProvider(wxString video,wxString subtitles,double fps) {
// Check if avisynth is available
bool avisynthAvailable = false;
bool dshowAvailable = false;
@ -106,7 +106,7 @@ VideoProvider *VideoProvider::GetProvider(wxString video,wxString subtitles) {
// Use DirectShow provider
if (!provider && (preffered == _T("dshow") || !avisynthAvailable)) {
try {
provider = new DirectShowVideoProvider(video,subtitles);
provider = new DirectShowVideoProvider(video,subtitles,fps);
}
catch (...) {
delete provider;
@ -119,7 +119,7 @@ VideoProvider *VideoProvider::GetProvider(wxString video,wxString subtitles) {
// Use Avisynth provider
if (!provider) {
try {
provider = new AvisynthVideoProvider(video,subtitles);
provider = new AvisynthVideoProvider(video,subtitles,fps);
}
catch (...) {
delete provider;

View File

@ -65,5 +65,7 @@ public:
virtual int GetSourceWidth()=0; // Returns the original source width in pixels
virtual int GetSourceHeight()=0; // Returns the original source height in pixels
static VideoProvider *GetProvider(wxString video,wxString subtitles);
virtual void OverrideFrameTimeList(wxArrayInt list) {} // Override the list with the provided one, for VFR handling
static VideoProvider *GetProvider(wxString video,wxString subtitles,double fps=0.0);
};

View File

@ -39,6 +39,7 @@
#include "video_provider_avs.h"
#include "options.h"
#include "main.h"
#include "vfr.h"
#ifdef __WINDOWS__
@ -46,17 +47,19 @@
///////////////
// Constructor
AvisynthVideoProvider::AvisynthVideoProvider(wxString _filename, wxString _subfilename) {
AvisynthVideoProvider::AvisynthVideoProvider(wxString _filename, wxString _subfilename, double _fps) {
AVSTRACE(wxString::Format(_T("AvisynthVideoProvider: Creating new AvisynthVideoProvider: \"%s\", \"%s\""), _filename, _subfilename));
bool mpeg2dec3_priority = true;
RGB32Video = NULL;
SubtitledVideo = NULL;
ResizedVideo = NULL;
data = NULL;
fps = _fps;
depth = 0;
last_fnum = -1;
num_frames = 0;
subfilename = _subfilename;
zoom = 1.0;
@ -219,7 +222,12 @@ PClip AvisynthVideoProvider::OpenVideo(wxString _filename, bool mpeg2dec3_priori
dss2 = false;
if (env->FunctionExists("dss2")) {
AVSTRACE(_T("visynthVideoProvider::OpenVideo: Invoking DSS2"));
script = env->Invoke("DSS2", videoFilename);
if (fps == 0.0) script = env->Invoke("DSS2", videoFilename);
else {
const char *argnames[2] = { 0, "fps" };
AVSValue args[2] = { videoFilename, fps };
script = env->Invoke("DSS2", AVSValue(args,2), argnames);
}
AVSTRACE(_T("visynthVideoProvider::OpenVideo: Successfully opened file with DSS2"));
dss2 = true;
}
@ -333,7 +341,18 @@ PClip AvisynthVideoProvider::ApplyDARZoom(double _zoom, double _dar, PClip video
////////////////////////
// Actually get a frame
wxBitmap AvisynthVideoProvider::GetFrame(int n, bool force) {
wxBitmap AvisynthVideoProvider::GetFrame(int _n, bool force) {
// Transform n if overriden
int n = _n;
if (frameTime.Count()) {
if (n < 0) n = 0;
if (n >= (signed) frameTime.Count()) n = frameTime.Count()-1;
int time = frameTime[n];
double curFps = (double)vi.fps_numerator/(double)vi.fps_denominator;
n = time * curFps / 1000.0;
}
// Get frame
AVSTRACE(_T("AvisynthVideoProvider::GetFrame"));
if (n != last_fnum || force) {
wxMutexLocker lock(AviSynthMutex);
@ -464,4 +483,12 @@ void AvisynthVideoProvider::LoadVSFilter() {
}
////////////////////////
// Override frame times
void AvisynthVideoProvider::OverrideFrameTimeList(wxArrayInt list) {
frameTime = list;
num_frames = frameTime.Count();
}
#endif

View File

@ -65,6 +65,7 @@ private:
wxString subfilename;
int last_fnum;
int num_frames;
int depth;
@ -73,6 +74,8 @@ private:
double dar;
double zoom;
double fps;
wxArrayInt frameTime;
PClip RGB32Video;
PClip SubtitledVideo;
@ -86,7 +89,7 @@ private:
void AttachOverlay(SubtitleProvider::Overlay *_overlay) {}
public:
AvisynthVideoProvider(wxString _filename, wxString _subfilename);
AvisynthVideoProvider(wxString _filename, wxString _subfilename, double fps=0.0);
~AvisynthVideoProvider();
void RefreshSubtitles();
@ -98,7 +101,7 @@ public:
// properties
int GetPosition() { return last_fnum; };
int GetFrameCount() { return vi.num_frames; };
int GetFrameCount() { return num_frames? num_frames: vi.num_frames; };
double GetFPS() { return (double)vi.fps_numerator/(double)vi.fps_denominator; };
int GetWidth() { return vi.width; };
@ -107,6 +110,8 @@ public:
int GetSourceWidth() { return RGB32Video->GetVideoInfo().width; };
int GetSourceHeight() { return RGB32Video->GetVideoInfo().height; };
void OverrideFrameTimeList(wxArrayInt list);
};
#endif

View File

@ -55,9 +55,10 @@ DEFINE_GUID(CLSID_VideoSink, 0xf13d3732, 0x96bd, 0x4108, 0xaf, 0xeb, 0xe8, 0x5f,
///////////////
// Constructor
// Based on Haali's code for DirectShowSource2
DirectShowVideoProvider::DirectShowVideoProvider(wxString _filename, wxString _subfilename) {
DirectShowVideoProvider::DirectShowVideoProvider(wxString _filename, wxString _subfilename,double _fps) {
zoom = 1.0;
dar = 4.0/3.0;
fps = _fps;
m_hFrameReady = CreateEvent(NULL, FALSE, FALSE, NULL);
OpenVideo(_filename);
}
@ -260,13 +261,6 @@ HRESULT DirectShowVideoProvider::OpenVideo(wxString _filename) {
// Get video duration
if (FAILED(hr = ms->GetDuration(&duration))) return hr;
// Length of each frame? (??)
if (defd == 0) defd = 417083;
// No clue, either
int avgf = 0;
if (avgf > 0) defd = avgf;
// Set pixel type
//switch (type) {
// case IVS_RGB32: m_vi.pixel_type = VideoInfo::CS_BGR32; break;
@ -275,13 +269,14 @@ HRESULT DirectShowVideoProvider::OpenVideo(wxString _filename) {
// default: return E_FAIL;
//}
// Set number of frames and fps
// Set FPS and frame duration
if (defd == 0) defd = 417083;
if (fps != 0.0) defd = long long (10000000.0 / fps);
else fps = 10000000.0 / double(defd);
// Set number of frames
last_fnum = -1;
num_frames = duration / defd;
fps = 10000000.0 / double(defd);
// Set frame length
//m_avgframe = defd;
// Store filters
m_pR = sink;
@ -401,34 +396,45 @@ void DirectShowVideoProvider::ReadFrame(long long timestamp, unsigned format, un
/////////////////////
// Get Next DS Frame
bool DirectShowVideoProvider::NextFrame(DF &_df,int &_fn) {
int DirectShowVideoProvider::NextFrame(DF &_df,int &_fn) {
// Keep reading until it gets a good frame
while (true) {
if (WaitForSingleObject(m_hFrameReady, INFINITE) != WAIT_OBJECT_0) return false;
// Set object to receive data
// Set object and receive data
DF df;
if (WaitForSingleObject(m_hFrameReady, INFINITE) != WAIT_OBJECT_0) return 1;
// Read frame
HRESULT hr = m_pR->ReadFrame(ReadFrame, &df);
if (FAILED(hr)) return false;
if (FAILED(hr)) return 2;
// End of file
if (hr == S_FALSE) return false;
if (hr == S_FALSE) return 3;
// Valid timestamp
if (df.timestamp >= 0) {
// Frame number
int frameno = (int)((double)df.timestamp / defd + 0.5);
// CFR frame number
int frameno = -1;
if (frameTime.Count() == 0) frameno = (int)((double)df.timestamp / defd + 0.5);
// VFR
else {
for (unsigned int i=0;i<frameTime.Count();i++) {
if (df.timestamp < (long long) frameTime[i] * 10000) {
frameno = i-1;
break;
}
}
if (frameno == -1) frameno = frameTime.Count()-1;
}
// Got a good one
if (frameno >= 0 && frameno <= (signed) num_frames) {
if (frameno >= 0) {
_fn = frameno;
_df = df;
if (zoom != 1.0 || dar != 1.0) {
_df.frame.Rescale(height*zoom*dar,height*zoom,wxIMAGE_QUALITY_NORMAL);
}
return true;
return 0;
}
}
}
@ -440,6 +446,7 @@ bool DirectShowVideoProvider::NextFrame(DF &_df,int &_fn) {
wxBitmap DirectShowVideoProvider::GetFrame(int n) {
// Normalize frame number
if (n >= (signed) num_frames) n = num_frames-1;
if (n < 0) n = 0;
// Current
if (n == last_fnum) return wxBitmap(rdf.frame);
@ -447,7 +454,11 @@ wxBitmap DirectShowVideoProvider::GetFrame(int n) {
// Variables
DF df;
int fn;
REFERENCE_TIME cur = defd * n + 10001;
// Time to seek to
REFERENCE_TIME cur;
cur = defd * n + 10001;
if (frameTime.Count() > (unsigned) n) cur = frameTime[n] * 10000 + 10001;
if (cur < 0) cur = 0;
// Is next
@ -472,14 +483,14 @@ seek:
while (true) {
// Get frame
DF df;
int fn;
NextFrame(df,fn);
int fn = -1;
int result = NextFrame(df,fn);
// Preroll
if (fn < n) continue;
if (result == 0 && fn < n) continue;
// Right frame
if (fn == n) {
else if (fn == n) {
// we want this frame, compare timestamps to account for decimation
// we see this for the first time
if (rdf.timestamp < 0) rdf.timestamp = df.timestamp;
@ -492,11 +503,15 @@ seek:
break;
}
// Passed, seek back and try again
else {
// Passed or end of file, seek back and try again
else if (result == 0 || result == 3) {
cur -= defd;
goto seek;
//return wxBitmap(width,height);
}
// Failed
else {
return wxBitmap(height*zoom*dar,height*zoom);
}
}
@ -534,4 +549,12 @@ void DirectShowVideoProvider::GetFloatFrame(float* Buffer, int n) {
}
////////////////////////
// Override frame times
void DirectShowVideoProvider::OverrideFrameTimeList(wxArrayInt list) {
frameTime = list;
num_frames = frameTime.Count();
}
#endif

View File

@ -68,6 +68,7 @@ class DirectShowVideoProvider: public VideoProvider {
private:
wxString subfilename;
wxArrayInt frameTime;
unsigned int last_fnum;
unsigned int width;
@ -89,7 +90,7 @@ private:
void CloseVideo();
static void ReadFrame(long long timestamp, unsigned format, unsigned bpp, const unsigned char *frame, unsigned width, unsigned height, unsigned stride, unsigned arx, unsigned ary, void *arg);
bool NextFrame(DF &df,int &fn);
int NextFrame(DF &df,int &fn);
void RegROT();
void UnregROT();
@ -104,7 +105,7 @@ private:
DWORD m_rot_cookie;
public:
DirectShowVideoProvider(wxString _filename, wxString _subfilename);
DirectShowVideoProvider(wxString _filename, wxString _subfilename,double _fps=0.0);
~DirectShowVideoProvider();
void RefreshSubtitles();
@ -124,6 +125,8 @@ public:
int GetSourceWidth() { return width; };
int GetSourceHeight() { return height; };
void OverrideFrameTimeList(wxArrayInt list);
};
#endif