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 // Check if it's CFR
bool isCFR = true; bool isCFR = true;
double estimateCFR = timecodes.back() / timecodes.size()-1; double estimateCFR = timecodes.back() / timecodes.size()-1;
double curTime = 0; double t1,t2;
for (int i=0;i<frames;i++) { for (int i=1;i<frames;i++) {
int delta = int(curTime - timecodes[i]); t1 = timecodes[i];
if (abs(delta > 1)) { t2 = timecodes[i-1];
int delta = int(t1 - t2 - estimateCFR);
if (abs(delta > 2)) {
isCFR = false; isCFR = false;
break; break;
} }
curTime += estimateCFR;
} }
// Constant framerate // Constant framerate
if (isCFR) { if (isCFR) {
if (abs(estimateCFR - 23.976) < 0.01) estimateCFR = 23.976; if (abs(estimateCFR - 23.976) < 0.01) estimateCFR = 24000.0 / 1001.0;
if (abs(estimateCFR - 29.97) < 0.01) estimateCFR = 29.97; if (abs(estimateCFR - 29.97) < 0.01) estimateCFR = 30000.0 / 1001.0;
target.SetCFR(estimateCFR); 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 // Globals
FrameRate VFR_Output; FrameRate VFR_Output;

View file

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

View file

@ -163,14 +163,9 @@ void VideoDisplay::SetVideo(const wxString &filename) {
if (!filename.IsEmpty()) { if (!filename.IsEmpty()) {
try { try {
grid->CommitChanges(true); 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 // Read extra data from file
bool mkvOpen = MatroskaWrapper::wrapper.IsOpen(); bool mkvOpen = MatroskaWrapper::wrapper.IsOpen();
wxString ext = filename.Right(4).Lower(); wxString ext = filename.Right(4).Lower();
@ -186,7 +181,11 @@ void VideoDisplay::SetVideo(const wxString &filename) {
// Ask to override timecodes // Ask to override timecodes
int override = wxYES; 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 (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 // Close mkv
MatroskaWrapper::wrapper.Close(); MatroskaWrapper::wrapper.Close();
@ -198,6 +197,13 @@ void VideoDisplay::SetVideo(const wxString &filename) {
} }
#endif #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 // Update size
UpdateSize(); UpdateSize();

View file

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

View file

@ -65,5 +65,7 @@ public:
virtual int GetSourceWidth()=0; // Returns the original source width in pixels virtual int GetSourceWidth()=0; // Returns the original source width in pixels
virtual int GetSourceHeight()=0; // Returns the original source height 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 "video_provider_avs.h"
#include "options.h" #include "options.h"
#include "main.h" #include "main.h"
#include "vfr.h"
#ifdef __WINDOWS__ #ifdef __WINDOWS__
@ -46,17 +47,19 @@
/////////////// ///////////////
// Constructor // 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)); AVSTRACE(wxString::Format(_T("AvisynthVideoProvider: Creating new AvisynthVideoProvider: \"%s\", \"%s\""), _filename, _subfilename));
bool mpeg2dec3_priority = true; bool mpeg2dec3_priority = true;
RGB32Video = NULL; RGB32Video = NULL;
SubtitledVideo = NULL; SubtitledVideo = NULL;
ResizedVideo = NULL; ResizedVideo = NULL;
data = NULL; data = NULL;
fps = _fps;
depth = 0; depth = 0;
last_fnum = -1; last_fnum = -1;
num_frames = 0;
subfilename = _subfilename; subfilename = _subfilename;
zoom = 1.0; zoom = 1.0;
@ -219,7 +222,12 @@ PClip AvisynthVideoProvider::OpenVideo(wxString _filename, bool mpeg2dec3_priori
dss2 = false; dss2 = false;
if (env->FunctionExists("dss2")) { if (env->FunctionExists("dss2")) {
AVSTRACE(_T("visynthVideoProvider::OpenVideo: Invoking 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")); AVSTRACE(_T("visynthVideoProvider::OpenVideo: Successfully opened file with DSS2"));
dss2 = true; dss2 = true;
} }
@ -333,7 +341,18 @@ PClip AvisynthVideoProvider::ApplyDARZoom(double _zoom, double _dar, PClip video
//////////////////////// ////////////////////////
// Actually get a frame // 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")); AVSTRACE(_T("AvisynthVideoProvider::GetFrame"));
if (n != last_fnum || force) { if (n != last_fnum || force) {
wxMutexLocker lock(AviSynthMutex); 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 #endif

View file

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

View file

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

View file

@ -68,6 +68,7 @@ class DirectShowVideoProvider: public VideoProvider {
private: private:
wxString subfilename; wxString subfilename;
wxArrayInt frameTime;
unsigned int last_fnum; unsigned int last_fnum;
unsigned int width; unsigned int width;
@ -89,7 +90,7 @@ private:
void CloseVideo(); 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); 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 RegROT();
void UnregROT(); void UnregROT();
@ -104,7 +105,7 @@ private:
DWORD m_rot_cookie; DWORD m_rot_cookie;
public: public:
DirectShowVideoProvider(wxString _filename, wxString _subfilename); DirectShowVideoProvider(wxString _filename, wxString _subfilename,double _fps=0.0);
~DirectShowVideoProvider(); ~DirectShowVideoProvider();
void RefreshSubtitles(); void RefreshSubtitles();
@ -124,6 +125,8 @@ public:
int GetSourceWidth() { return width; }; int GetSourceWidth() { return width; };
int GetSourceHeight() { return height; }; int GetSourceHeight() { return height; };
void OverrideFrameTimeList(wxArrayInt list);
}; };
#endif #endif