diff --git a/aegisub/build/aegisub_vs2008/aegisub_vs2008.vcproj b/aegisub/build/aegisub_vs2008/aegisub_vs2008.vcproj index fc9754759..26ebc056c 100644 --- a/aegisub/build/aegisub_vs2008/aegisub_vs2008.vcproj +++ b/aegisub/build/aegisub_vs2008/aegisub_vs2008.vcproj @@ -601,14 +601,6 @@ RelativePath="..\..\src\mkv_wrap.h" > - - - - - -#include "vfw_wrap.h" - - -/// @brief Get keyframe list -/// @param filename -/// -std::vector VFWWrapper::GetKeyFrames(wxString filename) { - std::vector frames; - -#ifdef __WINDOWS__ - // Init vfw - AVIFileInit(); - - // Open file - PAVIFILE pfile; - long hr = AVIFileOpen(&pfile, filename.wc_str(), OF_SHARE_DENY_WRITE, 0); - if (hr != 0) { - AVIFileExit(); - switch (hr) { - case AVIERR_BADFORMAT: - throw _T("Unable to open AVI file for reading keyframes:\nThe file is corrupted, incomplete or has an otherwise bad format."); - case AVIERR_MEMORY: - throw _T("Unable to open AVI file for reading keyframes:\nThe file could not be opened because of insufficient memory."); - case AVIERR_FILEREAD: - throw _T("Unable to open AVI file for reading keyframes:\nAn error occurred reading the file. There might be a problem with the storage media."); - case AVIERR_FILEOPEN: - throw _T("Unable to open AVI file for reading keyframes:\nThe file could not be opened. It might be in use by another application, or you do not have permission to access it."); - case REGDB_E_CLASSNOTREG: - throw _T("Unable to open AVI file for reading keyframes:\nThere is no handler installed for the file extension. This might indicate a fundameltal problem in your Video for Windows installation, and can be caused by extremely stripped Windows installations."); - default: - throw _T("Unable to open AVI file for reading keyframes:\nUnknown error."); - } - } - - // Open stream - PAVISTREAM ppavi; - hr = AVIFileGetStream(pfile,&ppavi,streamtypeVIDEO,0); - if (hr != 0) { - AVIFileRelease(pfile); - AVIFileExit(); - switch (hr) { - case AVIERR_NODATA: - throw _T("Unable to open AVI video stream for reading keyframes:\nThe file does not contain a usable video stream."); - case AVIERR_MEMORY: - throw _T("Unable to open AVI video stream for reading keyframes:\nNot enough memory."); - default: - throw _T("Unable to open AVI video stream for reading keyframes:\nUnknown error."); - } - - } - - // Get stream data - AVISTREAMINFO avis; - hr = AVIStreamInfo(ppavi,&avis,sizeof(avis)); - if (hr != 0) { - AVIStreamRelease(ppavi); - AVIFileRelease(pfile); - AVIFileExit(); - throw _T("Unable to read keyframes from AVI file:\nCould not get stream information."); - } - size_t frame_c = avis.dwLength; - - // Loop through stream - for (size_t i=0;i -#endif - - -/// DOCME -/// @class VFWWrapper -/// @brief DOCME -/// -/// DOCME -class VFWWrapper { -public: - static std::vector GetKeyFrames(wxString filename); -}; - - diff --git a/aegisub/src/video_provider_avs.cpp b/aegisub/src/video_provider_avs.cpp index 7d35288ee..eb9776a63 100644 --- a/aegisub/src/video_provider_avs.cpp +++ b/aegisub/src/video_provider_avs.cpp @@ -37,40 +37,128 @@ #include "config.h" #ifdef WITH_AVISYNTH + #ifndef AGI_PRE #include #include #endif +#ifdef _WIN32 +#include +#endif + #include "charset_conv.h" #include "compat.h" -#include "gl_wrap.h" #include -#include "mkv_wrap.h" #include "standard_paths.h" -#include "vfw_wrap.h" -#include "video_context.h" #include "video_provider_avs.h" -/// @brief Constructor -/// @param _filename -/// AvisynthVideoProvider::AvisynthVideoProvider(wxString filename) try -: usedDirectShow(false) -, decoderName(_("Unknown")) -, num_frames(0) -, last_fnum(-1) -, RGB32Video(NULL) +: last_fnum(-1) { - RGB32Video = OpenVideo(filename); + iframe.flipped = true; + iframe.invertChannels = true; + wxMutexLocker lock(AviSynthMutex); + + wxFileName fname(filename); + if (!fname.FileExists()) + throw agi::FileNotFoundError(STD_STR(filename)); + + wxString extension = filename.Right(4).Lower(); + +#ifdef _WIN32 + if (extension == ".avi") { + // Try to read the keyframes before actually opening the file as trying + // to open the file while it's already open can cause problems with + // badly written VFW decoders + AVIFileInit(); + + PAVIFILE pfile; + long hr = AVIFileOpen(&pfile, filename.wc_str(), OF_SHARE_DENY_WRITE, 0); + if (hr) { + warning = "Unable to open AVI file for reading keyframes:\n"; + switch (hr) { + case AVIERR_BADFORMAT: + warning += "The file is corrupted, incomplete or has an otherwise bad format."; + break; + case AVIERR_MEMORY: + warning += "The file could not be opened because of insufficient memory."; + break; + case AVIERR_FILEREAD: + warning += "An error occurred reading the file. There might be a problem with the storage media."; + break; + case AVIERR_FILEOPEN: + warning += "The file could not be opened. It might be in use by another application, or you do not have permission to access it."; + break; + case REGDB_E_CLASSNOTREG: + warning += "There is no handler installed for the file extension. This might indicate a fundameltal problem in your Video for Windows installation, and can be caused by extremely stripped Windows installations."; + break; + default: + warning += "Unknown error."; + break; + } + goto file_exit; + } + + PAVISTREAM ppavi; + if (hr = AVIFileGetStream(pfile, &ppavi, streamtypeVIDEO, 0)) { + warning = "Unable to open AVI video stream for reading keyframes:\n"; + switch (hr) { + case AVIERR_NODATA: + warning += "The file does not contain a usable video stream."; + break; + case AVIERR_MEMORY: + warning += "Not enough memory."; + break; + default: + warning += "Unknown error."; + break; + } + goto file_release; + } + + AVISTREAMINFO avis; + if (AVIStreamInfo(ppavi,&avis,sizeof(avis))) { + warning = "Unable to read keyframes from AVI file:\nCould not get stream information."; + goto stream_release; + } + + bool all_keyframes = true; + for (size_t i = 0; i < avis.dwLength; i++) { + if (AVIStreamIsKeyFrame(ppavi, i)) + KeyFrames.push_back(i); + else + all_keyframes = false; + } + + // If every frame is a keyframe then just discard the keyframe data as it's useless + if (all_keyframes) KeyFrames.clear(); + + // Clean up +stream_release: + AVIStreamRelease(ppavi); +file_release: + AVIFileRelease(pfile); +file_exit: + AVIFileExit(); + } +#endif + + AVSValue script = Open(fname, extension); + + // Check if video was loaded properly + if (!script.IsClip() || !script.AsClip()->GetVideoInfo().HasVideo()) + throw VideoNotSupported("No usable video found"); + + RGB32Video = (env->Invoke("Cache", env->Invoke("ConvertToRGB32", script))).AsClip(); vi = RGB32Video->GetVideoInfo(); + fps = (double)vi.fps_numerator / vi.fps_denominator; } catch (AvisynthError const& err) { throw VideoOpenError("Avisynth error: " + std::string(err.msg)); } -/// @brief Destructor AvisynthVideoProvider::~AvisynthVideoProvider() { iframe.Clear(); } @@ -79,33 +167,33 @@ AVSValue AvisynthVideoProvider::Open(wxFileName const& fname, wxString const& ex char *videoFilename = env->SaveString(fname.GetShortPath().mb_str(csConvLocal)); // Avisynth file, just import it - if (extension == L".avs") { + if (extension == ".avs") { LOG_I("avisynth/video") << "Opening .avs file with Import"; - decoderName = L"Import"; + decoderName = "Avisynth/Import"; return env->Invoke("Import", videoFilename); } // Open avi file with AviSource - if (extension == L".avi") { + if (extension == ".avi") { LOG_I("avisynth/video") << "Opening .avi file with AviSource"; try { const char *argnames[2] = { 0, "audio" }; AVSValue args[2] = { videoFilename, false }; - decoderName = L"AviSource"; + decoderName = "Avisynth/AviSource"; return env->Invoke("AviSource", AVSValue(args,2), argnames); } - // On Failure, fallback to DSS - catch (AvisynthError &) { - LOG_I("avisynth/video") << "Failed to open .avi file with AviSource, switching to DirectShowSource"; + catch (AvisynthError &err) { + LOG_E("avisynth/video") << err.msg; + LOG_I("avisynth/video") << "Failed to open .avi file with AviSource, trying DirectShowSource"; } } // Open d2v with mpeg2dec3 - if (extension == L".d2v" && env->FunctionExists("Mpeg2Dec3_Mpeg2Source")) { + if (extension == ".d2v" && env->FunctionExists("Mpeg2Dec3_Mpeg2Source")) { LOG_I("avisynth/video") << "Opening .d2v file with Mpeg2Dec3_Mpeg2Source"; AVSValue script = env->Invoke("Mpeg2Dec3_Mpeg2Source", videoFilename); - decoderName = L"Mpeg2Dec3_Mpeg2Source"; + decoderName = "Avisynth/Mpeg2Dec3_Mpeg2Source"; //if avisynth is 2.5.7 beta 2 or newer old mpeg2decs will crash without this if (env->FunctionExists("SetPlanarLegacyAlignment")) { @@ -116,19 +204,19 @@ AVSValue AvisynthVideoProvider::Open(wxFileName const& fname, wxString const& ex } // If that fails, try opening it with DGDecode - if (extension == L".d2v" && env->FunctionExists("DGDecode_Mpeg2Source")) { + if (extension == ".d2v" && env->FunctionExists("DGDecode_Mpeg2Source")) { LOG_I("avisynth/video") << "Opening .d2v file with DGDecode_Mpeg2Source"; - decoderName = L"DGDecode_Mpeg2Source"; - return env->Invoke("Mpeg2Source", videoFilename); + decoderName = "DGDecode_Mpeg2Source"; + return env->Invoke("Avisynth/Mpeg2Source", videoFilename); - //note that DGDecode will also have issues like if the version is too ancient but no sane person - //would use that anyway + //note that DGDecode will also have issues like if the version is too + // ancient but no sane person would use that anyway } - if (extension == L".d2v" && env->FunctionExists("Mpeg2Source")) { + if (extension == ".d2v" && env->FunctionExists("Mpeg2Source")) { LOG_I("avisynth/video") << "Opening .d2v file with other Mpeg2Source"; AVSValue script = env->Invoke("Mpeg2Source", videoFilename); - decoderName = L"Mpeg2Source"; + decoderName = "Avisynth/Mpeg2Source"; //if avisynth is 2.5.7 beta 2 or newer old mpeg2decs will crash without this if (env->FunctionExists("SetPlanarLegacyAlignment")) @@ -139,22 +227,22 @@ AVSValue AvisynthVideoProvider::Open(wxFileName const& fname, wxString const& ex // Try loading DirectShowSource2 if (!env->FunctionExists("dss2")) { - wxFileName dss2path(StandardPaths::DecodePath(_T("?data/avss.dll"))); + wxFileName dss2path(StandardPaths::DecodePath("?data/avss.dll")); if (dss2path.FileExists()) { - env->Invoke("LoadPlugin",env->SaveString(dss2path.GetFullPath().mb_str(csConvLocal))); + env->Invoke("LoadPlugin", env->SaveString(dss2path.GetFullPath().mb_str(csConvLocal))); } } // If DSS2 loaded properly, try using it if (env->FunctionExists("dss2")) { LOG_I("avisynth/video") << "Opening file with DSS2"; - decoderName = L"DSS2"; + decoderName = "Avisynth/DSS2"; return env->Invoke("DSS2", videoFilename); } // Try DirectShowSource // Load DirectShowSource.dll from app dir if it exists - wxFileName dsspath(StandardPaths::DecodePath(_T("?data/DirectShowSource.dll"))); + wxFileName dsspath(StandardPaths::DecodePath("?data/DirectShowSource.dll")); if (dsspath.FileExists()) { env->Invoke("LoadPlugin",env->SaveString(dsspath.GetFullPath().mb_str(csConvLocal))); } @@ -163,8 +251,8 @@ AVSValue AvisynthVideoProvider::Open(wxFileName const& fname, wxString const& ex if (env->FunctionExists("DirectShowSource")) { const char *argnames[3] = { 0, "video", "audio" }; AVSValue args[3] = { videoFilename, true, false }; - usedDirectShow = true; - decoderName = L"DirectShowSource"; + decoderName = "Avisynth/DirectShowSource"; + warning = "Warning! The file is being opened using Avisynth's DirectShowSource, which has unreliable seeking. Frame numbers might not match the real number. PROCEED AT YOUR OWN RISK!"; LOG_I("avisynth/video") << "Opening file with DirectShowSource"; return env->Invoke("DirectShowSource", AVSValue(args,3), argnames); } @@ -174,118 +262,21 @@ AVSValue AvisynthVideoProvider::Open(wxFileName const& fname, wxString const& ex throw VideoNotSupported("No function suitable for opening the video found"); } -/// @brief Actually open the video into Avisynth -/// @param _filename -/// @return -/// -PClip AvisynthVideoProvider::OpenVideo(wxString filename) { - wxMutexLocker lock(AviSynthMutex); - - wxFileName fname(filename); - if (!fname.FileExists()) - throw agi::FileNotFoundError(STD_STR(filename)); - - AVSValue script; - wxString extension = filename.Right(4).Lower(); - try { - script = Open(fname, extension); - } - catch (AvisynthError const& err) { - throw VideoOpenError("Avisynth error: " + std::string(err.msg)); - } - - // Check if video was loaded properly - if (!script.IsClip() || !script.AsClip()->GetVideoInfo().HasVideo()) { - throw VideoNotSupported("No usable video found"); - } - - // Read keyframes and timecodes from MKV file - bool mkvOpen = MatroskaWrapper::wrapper.IsOpen(); - KeyFrames.clear(); - if (extension == L".mkv" || mkvOpen) { - // Parse mkv - if (!mkvOpen) MatroskaWrapper::wrapper.Open(filename); - - // Get keyframes - KeyFrames = MatroskaWrapper::wrapper.GetKeyFrames(); - - MatroskaWrapper::wrapper.SetToTimecodes(vfr_fps); - - // Close mkv - MatroskaWrapper::wrapper.Close(); - } -// check if we have windows, if so we can load keyframes from AVI files using VFW -#ifdef __WINDOWS__ - else if (extension == L".avi") { - KeyFrames.clear(); - KeyFrames = VFWWrapper::GetKeyFrames(filename); - } -#endif /* __WINDOWS__ */ - - // Check if the file is all keyframes - bool isAllKeyFrames = true; - for (unsigned int i=1; iInvoke("ConvertToRGB32", script); - - // Cache - return (env->Invoke("Cache", script)).AsClip(); -} - -/// @brief Actually get a frame -/// @param _n -/// @return -/// const AegiVideoFrame AvisynthVideoProvider::GetFrame(int n) { - if (vfr_fps.IsLoaded()) { - n = real_fps.FrameAtTime(vfr_fps.TimeAtFrame(n)); - } - // Get avs frame + if (n == last_fnum) return iframe; + wxMutexLocker lock(AviSynthMutex); + PVideoFrame frame = RGB32Video->GetFrame(n,env); - int Bpp = vi.BitsPerPixel() / 8; + iframe.pitch = frame->GetPitch(); + iframe.w = frame->GetRowSize() / (vi.BitsPerPixel() / 8); + iframe.h = frame->GetHeight(); - // Aegisub's video frame - AegiVideoFrame &final = iframe; - final.flipped = true; - final.invertChannels = true; + iframe.Allocate(); - // Set size properties - final.pitch = frame->GetPitch(); - final.w = frame->GetRowSize() / Bpp; - final.h = frame->GetHeight(); + memcpy(iframe.data, frame->GetReadPtr(), iframe.pitch * iframe.h); - // Allocate - final.Allocate(); - - // Copy - memcpy(final.data,frame->GetReadPtr(),final.pitch * final.h); - - // Set last number last_fnum = n; - return final; + return iframe; } - -/// @brief Get warning -/// -wxString AvisynthVideoProvider::GetWarning() const { - if (usedDirectShow) return L"Warning! The file is being opened using Avisynth's DirectShowSource, which has unreliable seeking. Frame numbers might not match the real number. PROCEED AT YOUR OWN RISK!"; - else return L""; -} - -#endif +#endif // HAVE_AVISYNTH diff --git a/aegisub/src/video_provider_avs.h b/aegisub/src/video_provider_avs.h index b6a024fca..3fb739250 100644 --- a/aegisub/src/video_provider_avs.h +++ b/aegisub/src/video_provider_avs.h @@ -44,41 +44,17 @@ /// /// DOCME class AvisynthVideoProvider: public VideoProvider, AviSynthWrapper { - /// DOCME + AegiVideoFrame iframe; + wxString decoderName; + agi::vfr::Framerate fps; + std::vector KeyFrames; + wxString warning; + + PClip RGB32Video; VideoInfo vi; - /// DOCME - AegiVideoFrame iframe; - - - /// DOCME - bool usedDirectShow; - - /// DOCME - wxString rendererCallString; - - /// DOCME - wxString decoderName; - - - /// DOCME - int num_frames; - - /// DOCME int last_fnum; - - /// DOCME - agi::vfr::Framerate real_fps; - agi::vfr::Framerate vfr_fps; - - /// DOCME - std::vector KeyFrames; - - /// DOCME - PClip RGB32Video; - - PClip OpenVideo(wxString filename); AVSValue Open(wxFileName const& fname, wxString const& extension); public: @@ -88,12 +64,12 @@ public: const AegiVideoFrame GetFrame(int n); int GetPosition() const { return last_fnum; }; - int GetFrameCount() const { return num_frames? num_frames: vi.num_frames; }; - agi::vfr::Framerate GetFPS() const { return vfr_fps.IsLoaded() ? vfr_fps : real_fps; }; + int GetFrameCount() const { return vi.num_frames; }; + agi::vfr::Framerate GetFPS() const { return fps; }; int GetWidth() const { return vi.width; }; int GetHeight() const { return vi.height; }; std::vector GetKeyFrames() const { return KeyFrames; }; - wxString GetWarning() const; - wxString GetDecoderName() const { return wxString(L"Avisynth/") + decoderName; } + wxString GetWarning() const { return warning; } + wxString GetDecoderName() const { return decoderName; } }; #endif