// Copyright (c) 2006, Fredrik Mellbin // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of the Aegisub Group nor the names of its contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // Aegisub Project http://www.aegisub.org/ // // $Id$ /// @file video_provider_avs.cpp /// @brief Avisynth-based video provider /// @ingroup video_input /// #include "config.h" #ifdef WITH_AVISYNTH #ifndef AGI_PRE #include #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) { RGB32Video = OpenVideo(filename); vi = RGB32Video->GetVideoInfo(); } catch (AvisynthError const& err) { throw VideoOpenError("Avisynth error: " + std::string(err.msg)); } /// @brief Destructor AvisynthVideoProvider::~AvisynthVideoProvider() { iframe.Clear(); } AVSValue AvisynthVideoProvider::Open(wxFileName const& fname, wxString const& extension) { char *videoFilename = env->SaveString(fname.GetShortPath().mb_str(csConvLocal)); // Avisynth file, just import it if (extension == L".avs") { LOG_I("avisynth/video") << "Opening .avs file with Import"; decoderName = L"Import"; return env->Invoke("Import", videoFilename); } // Open avi file with AviSource if (extension == L".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"; 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"; } } // Open d2v with mpeg2dec3 if (extension == L".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"; //if avisynth is 2.5.7 beta 2 or newer old mpeg2decs will crash without this if (env->FunctionExists("SetPlanarLegacyAlignment")) { AVSValue args[2] = { script, true }; script = env->Invoke("SetPlanarLegacyAlignment", AVSValue(args,2)); } return script; } // If that fails, try opening it with DGDecode if (extension == L".d2v" && env->FunctionExists("DGDecode_Mpeg2Source")) { LOG_I("avisynth/video") << "Opening .d2v file with DGDecode_Mpeg2Source"; decoderName = L"DGDecode_Mpeg2Source"; return env->Invoke("Mpeg2Source", videoFilename); //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")) { LOG_I("avisynth/video") << "Opening .d2v file with other Mpeg2Source"; AVSValue script = env->Invoke("Mpeg2Source", videoFilename); decoderName = L"Mpeg2Source"; //if avisynth is 2.5.7 beta 2 or newer old mpeg2decs will crash without this if (env->FunctionExists("SetPlanarLegacyAlignment")) script = env->Invoke("SetPlanarLegacyAlignment", script); return script; } // Try loading DirectShowSource2 if (!env->FunctionExists("dss2")) { wxFileName dss2path(StandardPaths::DecodePath(_T("?data/avss.dll"))); if (dss2path.FileExists()) { 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"; return env->Invoke("DSS2", videoFilename); } // Try DirectShowSource // Load DirectShowSource.dll from app dir if it exists wxFileName dsspath(StandardPaths::DecodePath(_T("?data/DirectShowSource.dll"))); if (dsspath.FileExists()) { env->Invoke("LoadPlugin",env->SaveString(dsspath.GetFullPath().mb_str(csConvLocal))); } // Then try using DSS if (env->FunctionExists("DirectShowSource")) { const char *argnames[3] = { 0, "video", "audio" }; AVSValue args[3] = { videoFilename, true, false }; usedDirectShow = true; decoderName = L"DirectShowSource"; LOG_I("avisynth/video") << "Opening file with DirectShowSource"; return env->Invoke("DirectShowSource", AVSValue(args,3), argnames); } // Failed to find a suitable function LOG_E("avisynth/video") << "DSS function not found"; 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 wxMutexLocker lock(AviSynthMutex); PVideoFrame frame = RGB32Video->GetFrame(n,env); int Bpp = vi.BitsPerPixel() / 8; // Aegisub's video frame AegiVideoFrame &final = iframe; final.flipped = true; final.invertChannels = true; // Set size properties final.pitch = frame->GetPitch(); final.w = frame->GetRowSize() / Bpp; final.h = frame->GetHeight(); // Allocate final.Allocate(); // Copy memcpy(final.data,frame->GetReadPtr(),final.pitch * final.h); // Set last number last_fnum = n; return final; } /// @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