/// @brief Constructor /// @param _filename /// AvisynthAudioProvider::AvisynthAudioProvider(wxString filename) : filename(filename) { try { AVSValue script; wxMutexLocker lock(avs_wrapper.GetMutex()); wxFileName fn(filename); if (!fn.FileExists()) throw agi::FileNotFoundError(STD_STR(filename)); IScriptEnvironment *env = avs_wrapper.GetEnv(); // Include if (filename.EndsWith(".avs")) { char *fname = env->SaveString(fn.GetShortPath().mb_str(csConvLocal)); script = env->Invoke("Import", fname); } // Use DirectShowSource else { const char * argnames[3] = { 0, "video", "audio" }; AVSValue args[3] = { env->SaveString(fn.GetShortPath().mb_str(csConvLocal)), false, true }; // Load DirectShowSource.dll from app dir if it exists wxFileName dsspath(StandardPaths::DecodePath("?data/DirectShowSource.dll")); if (dsspath.FileExists()) { env->Invoke("LoadPlugin",env->SaveString(dsspath.GetShortPath().mb_str(csConvLocal))); } // Load audio with DSS if it exists if (env->FunctionExists("DirectShowSource")) { script = env->Invoke("DirectShowSource", AVSValue(args,3),argnames); } // Otherwise fail else { throw agi::AudioProviderOpenError("No suitable audio source filter found. Try placing DirectShowSource.dll in the Aegisub application directory.", 0); } } LoadFromClip(script); } catch (AvisynthError &err) { std::string errmsg(err.msg); if (errmsg.find("filter graph manager won't talk to me") != errmsg.npos) throw agi::AudioDataNotFoundError("Avisynth error: " + errmsg, 0); else throw agi::AudioProviderOpenError("Avisynth error: " + errmsg, 0); } }
/// @brief Read from environment /// @param _clip /// void AvisynthAudioProvider::LoadFromClip(AVSValue _clip) { AVSValue script; // Check if it has audio VideoInfo vi = _clip.AsClip()->GetVideoInfo(); if (!vi.HasAudio()) throw agi::AudioDataNotFoundError("No audio found.", 0); IScriptEnvironment *env = avs_wrapper.GetEnv(); // Convert to one channel char buffer[1024]; strcpy(buffer,lagi_wxString(OPT_GET("Audio/Downmixer")->GetString()).mb_str(csConvLocal)); script = env->Invoke(buffer, _clip); // Convert to 16 bits per sample script = env->Invoke("ConvertAudioTo16bit", script); vi = script.AsClip()->GetVideoInfo(); // Convert sample rate int setsample = OPT_GET("Provider/Audio/AVS/Sample Rate")->GetInt(); if (vi.SamplesPerSecond() < 32000) setsample = 44100; if (setsample != 0) { AVSValue args[2] = { script, setsample }; script = env->Invoke("ResampleAudio", AVSValue(args,2)); } // Set clip PClip tempclip = script.AsClip(); vi = tempclip->GetVideoInfo(); // Read properties channels = vi.AudioChannels(); num_samples = vi.num_audio_samples; sample_rate = vi.SamplesPerSecond(); bytes_per_sample = vi.BytesPerAudioSample(); float_samples = false; clip = tempclip; }
AVSValue AvisynthVideoProvider::Open(agi::fs::path const& filename) { IScriptEnvironment *env = avs.GetEnv(); char *videoFilename = env->SaveString(agi::fs::ShortName(filename).c_str()); // Avisynth file, just import it if (agi::fs::HasExtension(filename, "avs")) { LOG_I("avisynth/video") << "Opening .avs file with Import"; decoder_name = "Avisynth/Import"; return env->Invoke("Import", videoFilename); } // Open avi file with AviSource if (agi::fs::HasExtension(filename, "avi")) { LOG_I("avisynth/video") << "Opening .avi file with AviSource"; try { const char *argnames[2] = { 0, "audio" }; AVSValue args[2] = { videoFilename, false }; decoder_name = "Avisynth/AviSource"; return env->Invoke("AviSource", AVSValue(args,2), argnames); } // On Failure, fallback to DSS 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 (agi::fs::HasExtension(filename, "d2v") && env->FunctionExists("Mpeg2Dec3_Mpeg2Source")) { LOG_I("avisynth/video") << "Opening .d2v file with Mpeg2Dec3_Mpeg2Source"; auto script = env->Invoke("Mpeg2Dec3_Mpeg2Source", videoFilename); decoder_name = "Avisynth/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 (agi::fs::HasExtension(filename, "d2v") && env->FunctionExists("DGDecode_Mpeg2Source")) { LOG_I("avisynth/video") << "Opening .d2v file with DGDecode_Mpeg2Source"; decoder_name = "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 } if (agi::fs::HasExtension(filename, "d2v") && env->FunctionExists("Mpeg2Source")) { LOG_I("avisynth/video") << "Opening .d2v file with other Mpeg2Source"; AVSValue script = env->Invoke("Mpeg2Source", videoFilename); decoder_name = "Avisynth/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")) { auto dss2path(config::path->Decode("?data/avss.dll")); if (agi::fs::FileExists(dss2path)) env->Invoke("LoadPlugin", env->SaveString(agi::fs::ShortName(dss2path).c_str())); } // If DSS2 loaded properly, try using it if (env->FunctionExists("dss2")) { LOG_I("avisynth/video") << "Opening file with DSS2"; decoder_name = "Avisynth/DSS2"; return env->Invoke("DSS2", videoFilename); } // Try DirectShowSource // Load DirectShowSource.dll from app dir if it exists auto dsspath(config::path->Decode("?data/DirectShowSource.dll")); if (agi::fs::FileExists(dsspath)) env->Invoke("LoadPlugin", env->SaveString(agi::fs::ShortName(dsspath).c_str())); // Then try using DSS if (env->FunctionExists("DirectShowSource")) { const char *argnames[3] = { 0, "video", "audio" }; AVSValue args[3] = { videoFilename, true, false }; decoder_name = "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); } // Failed to find a suitable function LOG_E("avisynth/video") << "DSS function not found"; throw VideoNotSupported("No function suitable for opening the video found"); }
int main(int argc, TCHAR* argv[]) { SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED); printf("Usage: filmtester <avs filename> [duplicates_maxlength=2]\n"); printf("The program plays the AVS file and tests for frame duplicates\n\n"); int duplicates_maxlength = 2; if (argc < 2) { printf("No filename specified.\n\n"); return -1; } if (argc > 2) { duplicates_maxlength = _ttoi(argv[2]); printf("INFO: duplicates_maxlength set to %d\n", duplicates_maxlength); } IScriptEnvironment *env = CreateScriptEnvironment(); _tprintf(_T("Loading \"%s\" ...\n"), argv[1]); LPCSTR arg_names[1] = { nullptr }; AVSValue arg_vals[1] = { (LPCSTR)argv[1] }; clip = env->Invoke("import", AVSValue(arg_vals,1), arg_names).AsClip(); printf("AVS file loaded successfully.\n\n"); VideoInfo vi = clip->GetVideoInfo(); printf("VideoInfo:\n"); printf("-----------\n"); if (vi.HasVideo()) { printf("width x height: %dx%d\n", vi.width, vi.height); printf("num_frames: %d\n", vi.num_frames); printf("fps: %d/%d\n", vi.fps_numerator, vi.fps_denominator); std::string colorspace; if (vi.pixel_type & VideoInfo::CS_BGR) colorspace += "BGR, "; if (vi.pixel_type & VideoInfo::CS_YUV) colorspace += "YUV, "; if (vi.pixel_type & VideoInfo::CS_INTERLEAVED) colorspace += "INTERLEAVED, "; if (vi.pixel_type & VideoInfo::CS_PLANAR) colorspace += "PLANAR, "; if (colorspace.length() > 0) colorspace.erase(colorspace.length()-2); printf("colorspace: %s\n", colorspace.c_str()); std::string colorformat; if (vi.pixel_type & VideoInfo::CS_BGR24) colorformat += "BGR24, "; if (vi.pixel_type & VideoInfo::CS_BGR32) colorformat += "BGR32, "; if (vi.pixel_type & VideoInfo::CS_YUY2) colorformat += "YUY2, "; if (vi.pixel_type & VideoInfo::CS_YV12) colorformat += "YV12, "; if (vi.pixel_type & VideoInfo::CS_I420) colorformat += "I420 (IYUV), "; if (colorformat.length() > 0) colorformat.erase(colorformat.length()-2); else colorformat = "UNKNOWN"; printf("colorformat: %s\n", colorformat.c_str()); std::string imagetype; if (vi.image_type & VideoInfo::IT_BFF) imagetype += "BFF, "; if (vi.image_type & VideoInfo::IT_TFF) imagetype += "TFF, "; if (vi.image_type & VideoInfo::IT_FIELDBASED) imagetype += "FIELDBASED, "; if (imagetype.length() > 0) imagetype.erase(imagetype.length()-2); else imagetype = "UNKNOWN"; printf("image_type: %s\n", imagetype.c_str()); printf("bits per pixel: %d\n", vi.BitsPerPixel()); } else printf("NO VIDEO\n"); if (vi.HasAudio()) { printf("audio channels: %d\n", vi.nchannels); printf("sample_type: %x\n", vi.sample_type); printf("samples per second: %d\n", vi.audio_samples_per_second); printf("bytes per channel sample: %d\n", vi.BytesPerChannelSample()); printf("bytes per audio sample: %d\n", vi.BytesPerAudioSample()); printf("num_audio_samples: %lld\n", vi.num_audio_samples); } else printf("NO AUDIO\n"); printf("-----------\n\n"); if (!vi.HasVideo()) { printf("Can't start video playback for the sequence without video.\n\n"); return -1; } printf("Starting playback ...\n"); prev_frame = clip->GetFrame(0, env); int framesize = prev_frame->GetFrameBuffer()->GetDataSize(); printf("INFO: framesize = %d bytes.\n\n", framesize); InitializeCriticalSection(&cs); SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); int error_count = 0; int dup_start_frame = 0; bool flag_dup = false; std::vector<std::pair<int, int>> duplicates; for(int i=1; i<vi.num_frames; ++i) { EnterCriticalSection(&cs); dst = clip->GetFrame(i, env); const BYTE *src_ptr = prev_frame->GetFrameBuffer()->GetReadPtr(); const BYTE *dst_ptr = dst->GetFrameBuffer()->GetReadPtr(); if (!memcmp(src_ptr, dst_ptr, framesize)) { if (!flag_dup) { flag_dup = true; dup_start_frame = i-1; } } else if (flag_dup) { int length = (i-1) - dup_start_frame; if (length >= duplicates_maxlength) { printf("\rfilmtester: duplication interval: %d..%d" SPACES "\n", dup_start_frame, i-1); duplicates.push_back(std::make_pair(dup_start_frame, i-1)); error_count++; } flag_dup = false; } prev_frame = dst; LeaveCriticalSection(&cs); printf("\r[%5.1f%%] [%d errors] %d/%d frame processing", (float)((i+1)*100)/vi.num_frames, error_count, i+1, vi.num_frames); } EnterCriticalSection(&cs); if (flag_dup) { int i = vi.num_frames; int length = (i-1) - dup_start_frame; if (length >= duplicates_maxlength) { printf("\rfilmtester: duplication interval: %d..%d" SPACES "\n", dup_start_frame, i-1); duplicates.push_back(std::make_pair(dup_start_frame, i-1)); error_count++; } flag_dup = false; } printf("\rProcessing completed." SPACES "\n\n"); printf("%d errors\n", error_count); printf("\n"); if (error_count > 0) { printf("Erroneous intervals (%d):\n", duplicates.size()); for(auto it = duplicates.begin(); it != duplicates.end(); ++it) printf("%5d .. %d\n", it->first, it->second); printf("\n"); } dst = nullptr; prev_frame = nullptr; clip = nullptr; LeaveCriticalSection(&cs); DeleteCriticalSection(&cs); return error_count; }