From 5813033fa82cf8f6cb22f265c3e30dd080ef00a3 Mon Sep 17 00:00:00 2001 From: Rodrigo Braz Monteiro Date: Mon, 1 Jan 2007 03:29:20 +0000 Subject: [PATCH] Improved handling of VFR files with DirectShow and Avisynth providers. Originally committed to SVN as r667. --- aegisub/mkv_wrap.cpp | 15 +++-- aegisub/vfr.cpp | 108 +++++++++++++++++++++++++++++++ aegisub/vfr.h | 4 ++ aegisub/video_display.cpp | 22 ++++--- aegisub/video_provider.cpp | 6 +- aegisub/video_provider.h | 4 +- aegisub/video_provider_avs.cpp | 33 +++++++++- aegisub/video_provider_avs.h | 9 ++- aegisub/video_provider_dshow.cpp | 85 +++++++++++++++--------- aegisub/video_provider_dshow.h | 7 +- 10 files changed, 236 insertions(+), 57 deletions(-) diff --git a/aegisub/mkv_wrap.cpp b/aegisub/mkv_wrap.cpp index dbda0a555..7d6acca88 100644 --- a/aegisub/mkv_wrap.cpp +++ b/aegisub/mkv_wrap.cpp @@ -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 1)) { + double t1,t2; + for (int i=1;i 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); } diff --git a/aegisub/vfr.cpp b/aegisub/vfr.cpp index e718e9c57..d9ade0e3d 100644 --- a/aegisub/vfr.cpp +++ b/aegisub/vfr.cpp @@ -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 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 found; + + // Find the relative fps of each area + for (unsigned int i=1;i= frameRates[j]) { + curFps = frameRates[j]; + break; + } + } + + // See if it's on list + bool onList = false; + for (unsigned int j=0;j 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[j]) { + v1 = frameRates[j]; + break; + } + } + + // Re-insert obtained result + found.push_back(v1); + } + + return found.back(); +} + + /////////// // Globals FrameRate VFR_Output; diff --git a/aegisub/vfr.h b/aegisub/vfr.h index 7a7742fd2..ce252e226 100644 --- a/aegisub/vfr.h +++ b/aegisub/vfr.h @@ -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(); }; diff --git a/aegisub/video_display.cpp b/aegisub/video_display.cpp index c9ac1ca00..8426a6db6 100644 --- a/aegisub/video_display.cpp +++ b/aegisub/video_display.cpp @@ -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(); diff --git a/aegisub/video_provider.cpp b/aegisub/video_provider.cpp index 6eb9fd8e0..543daa21d 100644 --- a/aegisub/video_provider.cpp +++ b/aegisub/video_provider.cpp @@ -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; diff --git a/aegisub/video_provider.h b/aegisub/video_provider.h index 4f99dcb12..9965a9a34 100644 --- a/aegisub/video_provider.h +++ b/aegisub/video_provider.h @@ -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); }; diff --git a/aegisub/video_provider_avs.cpp b/aegisub/video_provider_avs.cpp index b4654a796..497d38e4b 100644 --- a/aegisub/video_provider_avs.cpp +++ b/aegisub/video_provider_avs.cpp @@ -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 diff --git a/aegisub/video_provider_avs.h b/aegisub/video_provider_avs.h index 1f11ddea7..42dc35df3 100644 --- a/aegisub/video_provider_avs.h +++ b/aegisub/video_provider_avs.h @@ -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 diff --git a/aegisub/video_provider_dshow.cpp b/aegisub/video_provider_dshow.cpp index e74894c16..b219f5117 100644 --- a/aegisub/video_provider_dshow.cpp +++ b/aegisub/video_provider_dshow.cpp @@ -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= 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 diff --git a/aegisub/video_provider_dshow.h b/aegisub/video_provider_dshow.h index faf4b5f67..69b2a549b 100644 --- a/aegisub/video_provider_dshow.h +++ b/aegisub/video_provider_dshow.h @@ -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