DummyVideoProvider::DummyVideoProvider(agi::fs::path const& filename) { if (!boost::starts_with(filename.string(), "?dummy")) throw agi::fs::FileNotFound(std::string("Attempted creating dummy video provider with non-dummy filename")); std::vector<std::string> toks; auto const& fields = filename.string().substr(7); boost::split(toks, fields, boost::is_any_of(":")); if (toks.size() != 8) throw VideoOpenError("Too few fields in dummy video parameter list"); size_t i = 0; double fps; int frames, width, height, red, green, blue; using agi::util::try_parse; if (!try_parse(toks[i++], &fps)) throw VideoOpenError("Unable to parse fps field in dummy video parameter list"); if (!try_parse(toks[i++], &frames)) throw VideoOpenError("Unable to parse framecount field in dummy video parameter list"); if (!try_parse(toks[i++], &width)) throw VideoOpenError("Unable to parse width field in dummy video parameter list"); if (!try_parse(toks[i++], &height)) throw VideoOpenError("Unable to parse height field in dummy video parameter list"); if (!try_parse(toks[i++], &red)) throw VideoOpenError("Unable to parse red colour field in dummy video parameter list"); if (!try_parse(toks[i++], &green)) throw VideoOpenError("Unable to parse green colour field in dummy video parameter list"); if (!try_parse(toks[i++], &blue)) throw VideoOpenError("Unable to parse blue colour field in dummy video parameter list"); bool pattern = toks[i] == "c"; Create(fps, frames, width, height, red, green, blue, pattern); }
bool Project::DoLoadSubtitles(agi::fs::path const& path, std::string encoding, ProjectProperties &properties) { try { if (encoding.empty()) encoding = CharSetDetect::GetEncoding(path); } catch (agi::UserCancelException const&) { return false; } catch (agi::fs::FileNotFound const&) { config::mru->Remove("Subtitle", path); ShowError(path.string() + " not found."); return false; } if (encoding != "binary") { // Try loading as timecodes and keyframes first since we can't // distinguish them based on filename alone, and just ignore failures // rather than trying to differentiate between malformed timecodes // files and things that aren't timecodes files at all try { DoLoadTimecodes(path); return false; } catch (...) { } try { DoLoadKeyframes(path); return false; } catch (...) { } } try { properties = context->subsController->Load(path, encoding); } catch (agi::UserCancelException const&) { return false; } catch (agi::fs::FileNotFound const&) { config::mru->Remove("Subtitle", path); ShowError(path.string() + " not found."); return false; } catch (agi::Exception const& e) { ShowError(e.GetMessage()); return false; } catch (std::exception const& e) { ShowError(std::string(e.what())); return false; } catch (...) { ShowError(wxString("Unknown error")); return false; } Selection sel; AssDialogue *active_line = nullptr; if (!context->ass->Events.empty()) { int row = mid<int>(0, properties.active_row, context->ass->Events.size() - 1); active_line = &*std::next(context->ass->Events.begin(), row); sel.insert(active_line); } context->selectionController->SetSelectionAndActive(std::move(sel), active_line); context->subsGrid->ScrollTo(properties.scroll_position); return true; }
/// @brief Constructor /// @param filename The filename to open YUV4MPEGVideoProvider::YUV4MPEGVideoProvider(agi::fs::path const& filename, std::string const&) { fps_rat.num = -1; fps_rat.den = 1; try { #ifdef WIN32 sf = _wfopen(filename.c_str(), L"rb"); #else sf = fopen(filename.c_str(), "rb"); #endif if (!sf) throw agi::fs::FileNotFound(filename); CheckFileFormat(); ParseFileHeader(ReadHeader(0)); if (w <= 0 || h <= 0) throw VideoOpenError("Invalid resolution"); if (fps_rat.num <= 0 || fps_rat.den <= 0) { fps_rat.num = 25; fps_rat.den = 1; LOG_D("provider/video/yuv4mpeg") << "framerate info unavailable, assuming 25fps"; } if (pixfmt == Y4M_PIXFMT_NONE) pixfmt = Y4M_PIXFMT_420JPEG; if (imode == Y4M_ILACE_NOTSET) imode = Y4M_ILACE_UNKNOWN; luma_sz = w * h; switch (pixfmt) { case Y4M_PIXFMT_420JPEG: case Y4M_PIXFMT_420MPEG2: case Y4M_PIXFMT_420PALDV: chroma_sz = (w * h) >> 2; break; case Y4M_PIXFMT_422: chroma_sz = (w * h) >> 1; break; /// @todo add support for more pixel formats default: throw VideoOpenError("Unsupported pixel format"); } frame_sz = luma_sz + chroma_sz*2; num_frames = IndexFile(); if (num_frames <= 0 || seek_table.empty()) throw VideoOpenError("Unable to determine file length"); fseeko(sf, 0, SEEK_SET); } catch (...) { if (sf) fclose(sf); throw; } }
/// @brief Generates an unique name for the ffms2 index file and prepares the cache folder if it doesn't exist /// @param filename The name of the source file /// @return Returns the generated filename. agi::fs::path FFmpegSourceProvider::GetCacheFilename(agi::fs::path const& filename) { // Get the size of the file to be hashed uintmax_t len = agi::fs::Size(filename); // Get the hash of the filename boost::crc_32_type hash; hash.process_bytes(filename.string().c_str(), filename.string().size()); // Generate the filename auto result = config::path->Decode("?local/ffms2cache/" + std::to_string(hash.checksum()) + "_" + std::to_string(len) + "_" + std::to_string(agi::fs::ModifiedTime(filename)) + ".ffindex"); // Ensure that folder exists agi::fs::CreateDirectory(result.parent_path()); return result; }
void SubsController::Save(agi::fs::path const& filename, std::string const& encoding) { const SubtitleFormat *writer = SubtitleFormat::GetWriter(filename); if (!writer) throw "Unknown file type."; int old_autosaved_commit_id = autosaved_commit_id, old_saved_commit_id = saved_commit_id; try { autosaved_commit_id = saved_commit_id = commit_id; // Have to set this now for the sake of things that want to save paths // relative to the script in the header this->filename = filename; config::path->SetToken("?script", filename.parent_path()); FileSave(); writer->WriteFile(context->ass, filename, encoding); } catch (...) { autosaved_commit_id = old_autosaved_commit_id; saved_commit_id = old_saved_commit_id; throw; } SetFileName(filename); }
std::unique_ptr<VideoProvider> VideoProviderFactory::GetProvider(agi::fs::path const& video_file, std::string const& colormatrix) { std::vector<std::string> factories = GetClasses(OPT_GET("Video/Provider")->GetString()); factories.insert(factories.begin(), "YUV4MPEG"); factories.insert(factories.begin(), "Dummy"); bool found = false; bool supported = false; std::string errors; errors.reserve(1024); for (auto const& factory : factories) { std::string err; try { auto provider = Create(factory, video_file, colormatrix); LOG_I("manager/video/provider") << factory << ": opened " << video_file; return provider->WantsCaching() ? agi::util::make_unique<VideoProviderCache>(std::move(provider)) : std::move(provider); } catch (agi::fs::FileNotFound const&) { err = "file not found."; // Keep trying other providers as this one may just not be able to // open a valid path } catch (VideoNotSupported const&) { found = true; err = "video is not in a supported format."; } catch (VideoOpenError const& ex) { supported = true; err = ex.GetMessage(); } catch (agi::vfr::Error const& ex) { supported = true; err = ex.GetMessage(); } errors += factory + ": " + err + "\n"; LOG_D("manager/video/provider") << factory << ": " << err; } // No provider could open the file LOG_E("manager/video/provider") << "Could not open " << video_file; std::string msg = "Could not open " + video_file.string() + ":\n" + errors; if (!found) throw agi::fs::FileNotFound(video_file.string()); if (!supported) throw VideoNotSupported(msg); throw VideoOpenError(msg); }
std::unique_ptr<VideoProvider> VideoProviderFactory::GetProvider(agi::fs::path const& filename, std::string const& colormatrix, agi::BackgroundRunner *br) { auto preferred = OPT_GET("Video/Provider")->GetString(); auto sorted = GetSorted(boost::make_iterator_range(std::begin(providers), std::end(providers)), preferred); bool found = false; bool supported = false; std::string errors; errors.reserve(1024); for (auto factory : sorted) { std::string err; try { auto provider = factory->create(filename, colormatrix, br); if (!provider) continue; LOG_I("manager/video/provider") << factory->name << ": opened " << filename; return provider->WantsCaching() ? CreateCacheVideoProvider(std::move(provider)) : std::move(provider); } catch (agi::fs::FileNotFound const&) { err = "file not found."; // Keep trying other providers as this one may just not be able to // open a valid path } catch (VideoNotSupported const&) { found = true; err = "video is not in a supported format."; } catch (VideoOpenError const& ex) { supported = true; err = ex.GetMessage(); } catch (agi::vfr::Error const& ex) { supported = true; err = ex.GetMessage(); } errors += std::string(factory->name) + ": " + err + "\n"; LOG_D("manager/video/provider") << factory->name << ": " << err; } // No provider could open the file LOG_E("manager/video/provider") << "Could not open " << filename; std::string msg = "Could not open " + filename.string() + ":\n" + errors; if (!found) throw agi::fs::FileNotFound(filename.string()); if (!supported) throw VideoNotSupported(msg); throw VideoOpenError(msg); }
void AssFile::InsertAttachment(agi::fs::path const& filename) { AssEntryGroup group = AssEntryGroup::GRAPHIC; auto ext = boost::to_lower_copy(filename.extension().string()); if (ext == ".ttf" || ext == ".ttc" || ext == ".pfb") group = AssEntryGroup::FONT; Attachments.emplace_back(filename, group); }
bool LoadFile(lua_State *L, agi::fs::path const& filename) { std::unique_ptr<std::istream> file(agi::io::Open(filename, true)); file->seekg(0, std::ios::end); size_t size = file->tellg(); file->seekg(0, std::ios::beg); std::string buff; buff.resize(size); // Discard the BOM if present file->read(&buff[0], 3); size_t start = file->gcount(); if (start == 3 && buff[0] == -17 && buff[1] == -69 && buff[2] == -65) { buff.resize(size - 3); start = 0; } file->read(&buff[start], size - start); if (!agi::fs::HasExtension(filename, "moon")) return luaL_loadbuffer(L, &buff[0], buff.size(), filename.string().c_str()) == 0; // We have a MoonScript file, so we need to load it with that // It might be nice to have a dedicated lua state for compiling // MoonScript to Lua if (luaL_dostring(L, "return require('moonscript').loadstring")) return false; // Leaves error message on stack lua_pushlstring(L, &buff[0], buff.size()); lua_pushstring(L, filename.string().c_str()); if (lua_pcall(L, 2, 2, 0)) return false; // Leaves error message on stack // loadstring returns nil, error on error or a function on success if (lua_isnil(L, 1)) { lua_remove(L, 1); return false; } lua_pop(L, 1); // Remove the extra nil for the stackchecker return true; }
void Project::LoadVideo(agi::fs::path path) { if (path.empty()) return; if (!DoLoadVideo(path)) return; if (OPT_GET("Video/Open Audio")->GetBool() && audio_file != video_file && video_provider->HasAudio()) DoLoadAudio(video_file, true); double dar = video_provider->GetDAR(); if (dar > 0) context->videoController->SetAspectRatio(dar); else context->videoController->SetAspectRatio(AspectRatio::Default); context->videoController->JumpToFrame(0); }
void VideoContext::LoadKeyframes(agi::fs::path const& filename) { if (filename == keyframes_filename || filename.empty()) return; try { keyframes = agi::keyframe::Load(filename); keyframes_filename = filename; KeyframesOpen(keyframes); config::mru->Add("Keyframes", filename); } catch (agi::keyframe::Error const& err) { wxMessageBox(to_wx(err.GetMessage()), "Error opening keyframes file", wxOK | wxICON_ERROR | wxCENTER, context->parent); config::mru->Remove("Keyframes", filename); } catch (agi::fs::FileSystemError const& err) { wxMessageBox(to_wx(err.GetMessage()), "Error opening keyframes file", wxOK | wxICON_ERROR | wxCENTER, context->parent); config::mru->Remove("Keyframes", filename); } }
void AudioController::OpenAudio(agi::fs::path const& url) { if (url.empty()) throw agi::InternalError("AudioController::OpenAudio() was passed an empty string. This must not happen.", 0); std::unique_ptr<AudioProvider> new_provider; try { new_provider = AudioProviderFactory::GetProvider(url); config::path->SetToken("?audio", url); } catch (agi::UserCancelException const&) { throw; } catch (...) { config::mru->Remove("Audio", url); throw; } CloseAudio(); provider = std::move(new_provider); try { player = AudioPlayerFactory::GetAudioPlayer(provider.get()); } catch (...) { provider.reset(); throw; } audio_url = url; config::mru->Add("Audio", url); try { AnnounceAudioOpen(provider.get()); } catch (...) { CloseAudio(); throw; } }
void VideoContext::LoadTimecodes(agi::fs::path const& filename) { if (filename == timecodes_filename || filename.empty()) return; try { ovr_fps = agi::vfr::Framerate(filename); timecodes_filename = filename; config::mru->Add("Timecodes", filename); OnSubtitlesCommit(0, std::set<const AssEntry*>()); TimecodesOpen(ovr_fps); } catch (agi::fs::FileSystemError const& err) { wxMessageBox(to_wx(err.GetMessage()), "Error opening timecodes file", wxOK | wxICON_ERROR | wxCENTER, context->parent); config::mru->Remove("Timecodes", filename); } catch (const agi::vfr::Error& e) { wxLogError("Timecode file parse error: %s", to_wx(e.GetMessage())); config::mru->Remove("Timecodes", filename); } }
AssAttachment::AssAttachment(agi::fs::path const& name, AssEntryGroup group) : filename(name.filename().string()) , group(group) { // SSA stuffs some information about the font in the embedded filename, but // nothing else uses it so just do the absolute minimum (0 is the encoding) if (boost::iends_with(filename.get(), ".ttf")) filename = filename.get().substr(0, filename.get().size() - 4) + "_0" + filename.get().substr(filename.get().size() - 4); std::vector<char> data; std::unique_ptr<std::istream> file(agi::io::Open(name, true)); file->seekg(0, std::ios::end); data.resize(file->tellg()); file->seekg(0, std::ios::beg); file->read(&data[0], data.size()); entry_data = (group == AssEntryGroup::FONT ? "fontname: " : "filename: ") + filename.get() + "\r\n"; entry_data = entry_data.get() + agi::ass::UUEncode(data); }
void TTXTSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const& filename, std::string const& encoding) const { target->LoadDefault(false); // Load XML document wxXmlDocument doc; if (!doc.Load(filename.wstring())) throw TTXTParseError("Failed loading TTXT XML file.", nullptr); // Check root node name if (doc.GetRoot()->GetName() != "TextStream") throw TTXTParseError("Invalid TTXT file.", nullptr); // Check version wxString verStr = doc.GetRoot()->GetAttribute("version", ""); int version = -1; if (verStr == "1.0") version = 0; else if (verStr == "1.1") version = 1; else throw TTXTParseError("Unknown TTXT version: " + from_wx(verStr), nullptr); // Get children AssDialogue *diag = nullptr; int lines = 0; for (wxXmlNode *child = doc.GetRoot()->GetChildren(); child; child = child->GetNext()) { // Line if (child->GetName() == "TextSample") { if ((diag = ProcessLine(child, diag, version))) { lines++; target->Line.push_back(*diag); } } // Header else if (child->GetName() == "TextStreamHeader") { ProcessHeader(child); } } // No lines? if (lines == 0) target->Line.push_back(*new AssDialogue); }
void AudioController::SaveClip(agi::fs::path const& filename, TimeRange const& range) const { int64_t start_sample = SamplesFromMilliseconds(range.begin()); int64_t end_sample = SamplesFromMilliseconds(range.end()); if (filename.empty() || start_sample > provider->GetNumSamples() || range.length() == 0) return; agi::io::Save outfile(filename, true); std::ofstream& out(outfile.Get()); size_t bytes_per_sample = provider->GetBytesPerSample() * provider->GetChannels(); size_t bufsize = (end_sample - start_sample) * bytes_per_sample; int intval; short shortval; out << "RIFF"; out.write((char*)&(intval=bufsize+36),4); out<< "WAVEfmt "; out.write((char*)&(intval=16),4); out.write((char*)&(shortval=1),2); out.write((char*)&(shortval=provider->GetChannels()),2); out.write((char*)&(intval=provider->GetSampleRate()),4); out.write((char*)&(intval=provider->GetSampleRate()*provider->GetChannels()*provider->GetBytesPerSample()),4); out.write((char*)&(intval=provider->GetChannels()*provider->GetBytesPerSample()),2); out.write((char*)&(shortval=provider->GetBytesPerSample()<<3),2); out << "data"; out.write((char*)&bufsize,4); //samples per read size_t spr = 65536 / bytes_per_sample; std::vector<char> buf(bufsize); for(int64_t i = start_sample; i < end_sample; i += spr) { size_t len = std::min<size_t>(spr, end_sample - i); provider->GetAudio(&buf[0], i, len); out.write(&buf[0], len * bytes_per_sample); } }
void TTXTSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const& encoding) const { // Convert to TTXT AssFile copy(*src); ConvertToTTXT(copy); // Create XML structure wxXmlDocument doc; wxXmlNode *root = new wxXmlNode(nullptr, wxXML_ELEMENT_NODE, "TextStream"); root->AddAttribute("version", "1.1"); doc.SetRoot(root); // Create header WriteHeader(root); // Create lines const AssDialogue *prev = nullptr; for (auto current : copy.Line | agi::of_type<AssDialogue>()) { WriteLine(root, prev, current); prev = current; } // Save XML doc.Save(filename.wstring()); }
bool TXTSubtitleFormat::CanWriteFile(agi::fs::path const& filename) const { auto str = filename.string(); return boost::iends_with(str, ".txt") && !(boost::iends_with(str, ".encore.txt") || boost::iends_with(str, ".transtation.txt")); }
void SubsController::SetFileName(agi::fs::path const& path) { filename = path; config::path->SetToken("?script", path.parent_path()); config::mru->Add("Subtitle", path); OPT_SET("Path/Last/Subtitles")->SetString(filename.parent_path().string()); }
void SubsController::Load(agi::fs::path const& filename, std::string charset) { try { if (charset.empty()) charset = CharSetDetect::GetEncoding(filename); } catch (agi::UserCancelException const&) { return; } // Make sure that file isn't actually a timecode file if (charset != "binary") { try { TextFileReader testSubs(filename, charset); std::string cur = testSubs.ReadLineFromFile(); if (boost::starts_with(cur, "# timecode")) { context->videoController->LoadTimecodes(filename); return; } } catch (...) { // if trying to load the file as timecodes fails it's fairly // safe to assume that it is in fact not a timecode file } } const SubtitleFormat *reader = SubtitleFormat::GetReader(filename); try { AssFile temp; reader->ReadFile(&temp, filename, charset); bool found_style = false; bool found_dialogue = false; // Check if the file has at least one style and at least one dialogue line for (auto const& line : temp.Line) { AssEntryGroup type = line.Group(); if (type == AssEntryGroup::STYLE) found_style = true; if (type == AssEntryGroup::DIALOGUE) found_dialogue = true; if (found_style && found_dialogue) break; } // And if it doesn't add defaults for each if (!found_style) temp.InsertLine(new AssStyle); if (!found_dialogue) temp.InsertLine(new AssDialogue); context->ass->swap(temp); } catch (agi::UserCancelException const&) { return; } catch (agi::fs::FileNotFound const&) { wxMessageBox(filename.wstring() + " not found.", "Error", wxOK | wxICON_ERROR | wxCENTER, context->parent); config::mru->Remove("Subtitle", filename); return; } catch (agi::Exception const& err) { wxMessageBox(to_wx(err.GetChainedMessage()), "Error", wxOK | wxICON_ERROR | wxCENTER, context->parent); return; } catch (std::exception const& err) { wxMessageBox(to_wx(err.what()), "Error", wxOK | wxICON_ERROR | wxCENTER, context->parent); return; } catch (...) { wxMessageBox("Unknown error", "Error", wxOK | wxICON_ERROR | wxCENTER, context->parent); return; } SetFileName(filename); // Push the initial state of the file onto the undo stack undo_stack.clear(); redo_stack.clear(); autosaved_commit_id = saved_commit_id = commit_id + 1; context->ass->Commit("", AssFile::COMMIT_NEW); // Save backup of file if (CanSave() && OPT_GET("App/Auto/Backup")->GetBool()) { auto path_str = OPT_GET("Path/Auto/Backup")->GetString(); agi::fs::path path; if (path_str.empty()) path = filename.parent_path(); else path = config::path->Decode(path_str); agi::fs::CreateDirectory(path); agi::fs::Copy(filename, path/(filename.stem().string() + ".ORIGINAL" + filename.extension().string())); } FileOpen(filename); }
void VideoContext::SetVideo(const agi::fs::path &filename) { Reset(); if (filename.empty()) { VideoOpen(); return; } bool commit_subs = false; try { provider.reset(new ThreadedFrameSource(filename, context->ass->GetScriptInfo("YCbCr Matrix"), this)); video_provider = provider->GetVideoProvider(); video_filename = filename; // Check that the script resolution matches the video resolution int sx = context->ass->GetScriptInfoAsInt("PlayResX"); int sy = context->ass->GetScriptInfoAsInt("PlayResY"); int vx = GetWidth(); int vy = GetHeight(); // If the script resolution hasn't been set at all just force it to the // video resolution if (sx == 0 && sy == 0) { context->ass->SetScriptInfo("PlayResX", std::to_string(vx)); context->ass->SetScriptInfo("PlayResY", std::to_string(vy)); commit_subs = true; } // If it has been set to something other than a multiple of the video // resolution, ask the user if they want it to be fixed else if (sx % vx != 0 || sy % vy != 0) { switch (OPT_GET("Video/Check Script Res")->GetInt()) { case 1: // Ask to change on mismatch if (wxYES != wxMessageBox( wxString::Format(_("The resolution of the loaded video and the resolution specified for the subtitles don't match.\n\nVideo resolution:\t%d x %d\nScript resolution:\t%d x %d\n\nChange subtitles resolution to match video?"), vx, vy, sx, sy), _("Resolution mismatch"), wxYES_NO | wxCENTER, context->parent)) break; // Fallthrough to case 2 case 2: // Always change script res context->ass->SetScriptInfo("PlayResX", std::to_string(vx)); context->ass->SetScriptInfo("PlayResY", std::to_string(vy)); commit_subs = true; break; default: // Never change break; } } keyframes = video_provider->GetKeyFrames(); // Set frame rate video_fps = video_provider->GetFPS(); if (ovr_fps.IsLoaded()) { int ovr = wxMessageBox(_("You already have timecodes loaded. Would you like to replace them with timecodes from the video file?"), _("Replace timecodes?"), wxYES_NO | wxICON_QUESTION); if (ovr == wxYES) { ovr_fps = agi::vfr::Framerate(); timecodes_filename.clear(); } } // Set aspect ratio double dar = video_provider->GetDAR(); if (dar > 0) SetAspectRatio(dar); // Set filename config::mru->Add("Video", filename); config::path->SetToken("?video", filename); // Show warning std::string warning = video_provider->GetWarning(); if (!warning.empty()) wxMessageBox(to_wx(warning), "Warning", wxICON_WARNING | wxOK); has_subtitles = false; if (agi::fs::HasExtension(filename, "mkv")) has_subtitles = MatroskaWrapper::HasSubtitles(filename); provider->LoadSubtitles(context->ass); VideoOpen(); KeyframesOpen(keyframes); TimecodesOpen(FPS()); } catch (agi::UserCancelException const&) { } catch (agi::fs::FileSystemError const& err) { config::mru->Remove("Video", filename); wxMessageBox(to_wx(err.GetMessage()), "Error setting video", wxOK | wxICON_ERROR | wxCENTER); } catch (VideoProviderError const& err) { wxMessageBox(to_wx(err.GetMessage()), "Error setting video", wxOK | wxICON_ERROR | wxCENTER); } if (commit_subs) context->ass->Commit(_("change script resolution"), AssFile::COMMIT_SCRIPTINFO); else JumpToFrame(0); }
AvisynthVideoProvider::AvisynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix) { agi::acs::CheckFileRead(filename); std::lock_guard<std::mutex> lock(avs.GetMutex()); #ifdef _WIN32 if (agi::fs::HasExtension(filename, "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.c_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 fundamental 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 (FAILED(AVIStreamInfo(ppavi,&avis,sizeof(avis)))) { warning = "Unable to read keyframes from AVI file:\nCould not get stream information."; goto stream_release; } for (size_t i = 0; i < avis.dwLength; i++) { if (AVIStreamIsKeyFrame(ppavi, i)) keyframes.push_back(i); } // If every frame is a keyframe then just discard the keyframe data as it's useless if (keyframes.size() == (size_t)avis.dwLength) keyframes.clear(); // Clean up stream_release: AVIStreamRelease(ppavi); file_release: AVIFileRelease(pfile); file_exit: AVIFileExit(); } #endif try { auto script = Open(filename); // Check if video was loaded properly if (!script.IsClip() || !script.AsClip()->GetVideoInfo().HasVideo()) throw VideoNotSupported("No usable video found"); vi = script.AsClip()->GetVideoInfo(); if (!vi.IsRGB()) { /// @todo maybe read ColorMatrix hints for d2v files? AVSValue args[2] = { script, "Rec601" }; bool force_bt601 = OPT_GET("Video/Force BT.601")->GetBool() || colormatrix == "TV.601"; bool bt709 = vi.width > 1024 || vi.height >= 600; if (bt709 && (!force_bt601 || colormatrix == "TV.709")) { args[1] = "Rec709"; colorspace = "TV.709"; } else colorspace = "TV.601"; const char *argnames[2] = { 0, "matrix" }; script = avs.GetEnv()->Invoke("ConvertToRGB32", AVSValue(args, 2), argnames); } else colorspace = "None"; RGB32Video = avs.GetEnv()->Invoke("Cache", 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)); } }