From edff7d6a2d13ea0da8236a707ab8b20726dbf0ad Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Fri, 31 Dec 2010 21:02:17 +0000 Subject: [PATCH] Rewrite keyframe loading and saving code and move it to libaegisub Originally committed to SVN as r5073. --- .../aegisub_vs2008/aegisub_vs2008.vcproj | 8 - .../libaegisub_vs2008.vcproj | 8 + aegisub/libaegisub/Makefile | 1 + aegisub/libaegisub/common/keyframe.cpp | 117 ++++++++++++ .../libaegisub/include/libaegisub/keyframe.h | 43 +++++ aegisub/src/Makefile | 1 - aegisub/src/frame_main.cpp | 1 - aegisub/src/frame_main_events.cpp | 2 +- aegisub/src/keyframe.cpp | 177 ------------------ aegisub/src/keyframe.h | 57 ------ aegisub/src/video_context.cpp | 26 ++- aegisub/src/video_context.h | 2 - 12 files changed, 186 insertions(+), 257 deletions(-) create mode 100644 aegisub/libaegisub/common/keyframe.cpp create mode 100644 aegisub/libaegisub/include/libaegisub/keyframe.h delete mode 100644 aegisub/src/keyframe.cpp delete mode 100644 aegisub/src/keyframe.h diff --git a/aegisub/build/aegisub_vs2008/aegisub_vs2008.vcproj b/aegisub/build/aegisub_vs2008/aegisub_vs2008.vcproj index 2270786ff..93ba7c201 100644 --- a/aegisub/build/aegisub_vs2008/aegisub_vs2008.vcproj +++ b/aegisub/build/aegisub_vs2008/aegisub_vs2008.vcproj @@ -1361,14 +1361,6 @@ - - - - diff --git a/aegisub/build/libaegisub_vs2008/libaegisub_vs2008.vcproj b/aegisub/build/libaegisub_vs2008/libaegisub_vs2008.vcproj index 88e58694c..1807af120 100644 --- a/aegisub/build/libaegisub_vs2008/libaegisub_vs2008.vcproj +++ b/aegisub/build/libaegisub_vs2008/libaegisub_vs2008.vcproj @@ -275,6 +275,10 @@ RelativePath="..\..\libaegisub\common\charset_ucd.cpp" > + + @@ -421,6 +425,10 @@ RelativePath="..\..\libaegisub\include\libaegisub\io.h" > + + diff --git a/aegisub/libaegisub/Makefile b/aegisub/libaegisub/Makefile index ab03550e1..fe87afa85 100644 --- a/aegisub/libaegisub/Makefile +++ b/aegisub/libaegisub/Makefile @@ -24,6 +24,7 @@ SRC = \ common/mru.cpp \ common/option.cpp \ common/option_visit.cpp \ + common/keyframe.cpp \ common/log.cpp \ common/validator.cpp \ common/vfr.cpp \ diff --git a/aegisub/libaegisub/common/keyframe.cpp b/aegisub/libaegisub/common/keyframe.cpp new file mode 100644 index 000000000..5230f60de --- /dev/null +++ b/aegisub/libaegisub/common/keyframe.cpp @@ -0,0 +1,117 @@ +// Copyright (c) 2011, Thomas Goyne +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// $Id$ + +/// @file keyframe.cpp +/// @see keyframe.h +/// @ingroup libaegisub +/// + + +#include "config.h" + +#ifndef LAGI_PRE +#include +#include +#endif + +#include "libaegisub/io.h" +#include "libaegisub/line_iterator.h" +#include "libaegisub/keyframe.h" +#include "libaegisub/vfr.h" + +static std::pair, double> agi_keyframes(std::istream &file) { + double fps; + std::string fps_str; + file >> fps_str; + file >> fps; + + if (!file.good() || fps_str != "fps") + throw agi::keyframe::Error("FPS not found"); + + std::vector ret; + std::copy(std::istream_iterator(file), std::istream_iterator(), std::back_inserter(ret)); + return make_pair(ret, fps); +} + +static std::pair, double> other_keyframes(std::istream &file, char (*func)(std::string const&)) { + int count = 0; + std::vector ret; + agi::line_iterator end; + for (agi::line_iterator iter(file); iter != end; ++iter) { + char c = tolower(func(*iter)); + if (c == 'i') { + ret.push_back(count++); + } + else if (c == 'p' || c == 'b') { + ++count; + } + } + return std::make_pair(ret, 0); +} + +char xvid(std::string const& line) { + return line.empty() ? 0 : line[0]; +} + +char divx(std::string const& line) { + char chrs[] = "IPB"; + for (int i = 0; i < 3; ++i) { + std::string::size_type pos = line.find(chrs[i]); + if (pos != line.npos) + return line[pos]; + } + return 0; +} + +char x264(std::string const& line) { + std::string::size_type pos = line.find("type:"); + if (pos == line.npos || pos + 5 >= line.size()) return 0; + return line[pos + 5]; +} + +template +static bool starts_with(std::string const& str, const char (&test)[N]) { + if (str.size() < N) return false; + return std::mismatch(str.begin(), str.begin() + N - 1, test).first == str.begin() + N - 1; +} + + +namespace agi { namespace keyframe { + + void Save(std::string const& filename, std::vector const& keyframes, vfr::Framerate const& fps) { + io::Save file(filename); + std::ofstream& of = file.Get(); + of << "# keyframe format v1" << std::endl; + of << "fps " << fps.FPS() << std::endl; + std::copy(keyframes.begin(), keyframes.end(), std::ostream_iterator(of, "\n")); +} + +std::pair, double> Load(std::string const& filename) { + std::auto_ptr file(io::Open(filename)); + std::istream &is(*file.get()); + + std::string header; + std::getline(is, header); + + if (header == "# keyframe format v1") return agi_keyframes(is); + if (starts_with(header, "# XviD 2pass stat file")) return other_keyframes(is, xvid); + if (starts_with(header, "##map version")) return other_keyframes(is, divx); + if (starts_with(header, "#options:")) return other_keyframes(is, x264); + + throw Error("Unknown keyframe format"); +} + +} } diff --git a/aegisub/libaegisub/include/libaegisub/keyframe.h b/aegisub/libaegisub/include/libaegisub/keyframe.h new file mode 100644 index 000000000..881d7296a --- /dev/null +++ b/aegisub/libaegisub/include/libaegisub/keyframe.h @@ -0,0 +1,43 @@ +// Copyright (c) 2011, Thomas Goyne +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// $Id$ + +/// @file keyframe.h +/// @see keyframe.cpp +/// @ingroup libaegisub +/// + +#if !defined(AGI_PRE) && !defined(LAGI_PRE) +#include +#endif + +#include "exception.h" + +namespace agi { + namespace vfr { class Framerate; } + namespace keyframe { + /// @brief Load a keyframe file + /// @param filename File to load + /// @return Pair of frame numbers which are keyframes and fps + std::pair, double> Load(std::string const& filename); + /// @brief Save keyframes to a file + /// @param filename File to save to + /// @param keyframes List of keyframes to save + /// @param fps Current fps that goes with the keyframes + void Save(std::string const& filename, std::vector const& keyframes, vfr::Framerate const& fps); + + DEFINE_SIMPLE_EXCEPTION_NOINNER(Error, Exception, "keyframe/error") + } +} diff --git a/aegisub/src/Makefile b/aegisub/src/Makefile index be861c50c..7299a5b62 100644 --- a/aegisub/src/Makefile +++ b/aegisub/src/Makefile @@ -194,7 +194,6 @@ SRC += \ help_button.cpp \ hotkeys.cpp \ kana_table.cpp \ - keyframe.cpp \ main.cpp \ md5.c \ mkv_wrap.cpp \ diff --git a/aegisub/src/frame_main.cpp b/aegisub/src/frame_main.cpp index 8b98a5c77..7bab5e90c 100644 --- a/aegisub/src/frame_main.cpp +++ b/aegisub/src/frame_main.cpp @@ -64,7 +64,6 @@ #include "frame_main.h" #include "help_button.h" #include "hotkeys.h" -#include "keyframe.h" #include "libresrc/libresrc.h" #include "main.h" #include "standard_paths.h" diff --git a/aegisub/src/frame_main_events.cpp b/aegisub/src/frame_main_events.cpp index 4d3f6d623..f0efe5d61 100644 --- a/aegisub/src/frame_main_events.cpp +++ b/aegisub/src/frame_main_events.cpp @@ -81,7 +81,7 @@ #include "frame_main.h" #include "hotkeys.h" #include "include/aegisub/audio_player.h" -#include "keyframe.h" +#include "libaegisub/charset_conv.h" #include "libresrc/libresrc.h" #include "main.h" #include "preferences.h" diff --git a/aegisub/src/keyframe.cpp b/aegisub/src/keyframe.cpp deleted file mode 100644 index c2a3d6268..000000000 --- a/aegisub/src/keyframe.cpp +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (c) 2007, Alysson Souza e Silva -// 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 keyframe.cpp -/// @brief Read and store video keyframe data -/// @ingroup video_input -/// - -#include "config.h" - -#ifndef AGI_PRE -#include -#endif - -#include "compat.h" -#include "keyframe.h" -#include "main.h" -#include "text_file_reader.h" -#include "text_file_writer.h" -#include "video_context.h" - -std::vector KeyFrameFile::Load(wxString filename) { - std::vector keyFrames; - TextFileReader file(filename,_T("ASCII")); - - wxString cur = file.ReadLineFromFile(); - // Detect type (Only Xvid, DivX, x264 and Aegisub's keyframe files are currently supported) - if (cur == _T("# keyframe format v1")) { OpenAegiKeyFrames(file, keyFrames); } - else if (cur.StartsWith(_T("# XviD 2pass stat file"))) { OpenXviDKeyFrames(file, keyFrames); } - else if (cur.StartsWith(_T("##map version"))) { OpenDivXKeyFrames(file, keyFrames); } - else if (cur.StartsWith(_T("#options:"))) { Openx264KeyFrames(file, keyFrames); } - else { throw(_T("Invalid or unsupported keyframes file.")); } - - config::mru->Add("Keyframes", STD_STR(filename)); - return keyFrames; -} - -void KeyFrameFile::Save(wxString filename, std::vector const& keyFrames) { - TextFileWriter file(filename,_T("ASCII")); - file.WriteLineToFile(_T("# keyframe format v1")); - file.WriteLineToFile(wxString::Format(_T("fps %f"),VideoContext::Get()->VFR_Input.FPS())); - - for (unsigned int i=0;iAdd("Keyframes", STD_STR(filename)); -} - -/// @brief Aegisub keyframes file -/// @param file -/// @param keyFrames -/// -void KeyFrameFile::OpenAegiKeyFrames(TextFileReader& file, std::vector& keyFrames) -{ - double fps; - wxString cur = file.ReadLineFromFile(); - - // Read header - if (cur.Left(4) != _T("fps ")) throw _T("Invalid keyframes file, missing FPS."); - cur = cur.Mid(4); - cur.ToDouble(&fps); - if (fps == 0.0) throw _T("Invalid FPS."); - - // Set FPS - if (!VideoContext::Get()->TimecodesLoaded()) { - VideoContext::Get()->ovrFPS = fps; - } - - // Read lines - while (file.HasMoreLines()) { - cur = file.ReadLineFromFile(); - if (!cur.IsEmpty() && !cur.StartsWith(_T("#")) && cur.IsNumber()) { - long temp; - cur.ToLong(&temp); - keyFrames.push_back(temp); - } - } -} - -/// @brief XviD stats file -/// @param file -/// @param keyFrames -/// -void KeyFrameFile::OpenXviDKeyFrames(TextFileReader& file, std::vector& keyFrames) -{ - wxString cur = file.ReadLineFromFile(); - unsigned int count = 0; - - // Read lines - while (file.HasMoreLines()) { - if (cur.StartsWith(_T("i"))) { - keyFrames.push_back(count); - count++; - } - else if (cur.StartsWith(_T("p")) || cur.StartsWith(_T("b"))) { - count++; - } - cur = file.ReadLineFromFile(); - } -} - -/// @brief DivX stats file -/// @param file -/// @param keyFrames -/// -void KeyFrameFile::OpenDivXKeyFrames(TextFileReader& file, std::vector& keyFrames) -{ - wxString cur = file.ReadLineFromFile(); - unsigned int count = 0; - - // Read lines - while (file.HasMoreLines()) - { - if (cur.Contains(_T("I"))) { - keyFrames.push_back(count); - count++; - } - else if (cur.Contains(_T("P")) || cur.Contains(_T("B"))) { - count++; - } - cur = file.ReadLineFromFile(); - } -} - -/// @brief x264 stats file -/// @param file -/// @param keyFrames -/// -void KeyFrameFile::Openx264KeyFrames(TextFileReader& file, std::vector& keyFrames) -{ - wxString cur = file.ReadLineFromFile(); - unsigned int count = 0; - size_t pos; - - // Read lines - while (file.HasMoreLines()) - { - pos = cur.Find(_T("type:")); - if (cur.Mid(pos,6).Right(1).Lower() == (_T("i"))) { - keyFrames.push_back(count); - count++; - } - else if (cur.Mid(pos,6).Right(1).Lower() == (_T("p")) || cur.Mid(pos,6).Right(1).Lower() == (_T("b"))) { - count++; - } - cur = file.ReadLineFromFile(); - } -} diff --git a/aegisub/src/keyframe.h b/aegisub/src/keyframe.h deleted file mode 100644 index 82af544da..000000000 --- a/aegisub/src/keyframe.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2007, Alysson Souza e Silva -// 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 keyframe.h -/// @see keyframe.cpp -/// @ingroup video_input -/// - -#include "text_file_reader.h" - - -/// DOCME -/// @class KeyFrameFile -/// @brief DOCME -/// -/// DOCME -class KeyFrameFile -{ -public: - static std::vector Load(wxString filename); - static void Save(wxString filename, std::vector const& keyframes); -private: - static void OpenAegiKeyFrames(TextFileReader& file, std::vector& keyFrames); - static void OpenXviDKeyFrames(TextFileReader& file, std::vector& keyFrames); - static void OpenDivXKeyFrames(TextFileReader& file, std::vector& keyFrames); - static void Openx264KeyFrames(TextFileReader& file, std::vector& keyFrames); -}; - - diff --git a/aegisub/src/video_context.cpp b/aegisub/src/video_context.cpp index fc9b59092..41b84d3b2 100644 --- a/aegisub/src/video_context.cpp +++ b/aegisub/src/video_context.cpp @@ -64,7 +64,7 @@ #include "include/aegisub/audio_player.h" #include "include/aegisub/audio_provider.h" #include "include/aegisub/video_provider.h" -#include "keyframe.h" +#include #include #include "main.h" #include "mkv_wrap.h" @@ -454,24 +454,30 @@ void VideoContext::SetAspectRatio(int type, double value) { void VideoContext::LoadKeyframes(wxString filename) { if (filename == keyFramesFilename || filename.empty()) return; try { - keyFrames = KeyFrameFile::Load(filename); + std::pair, double> kf = agi::keyframe::Load(STD_STR(filename)); + keyFrames = kf.first; keyFramesFilename = filename; KeyframesOpen(keyFrames); + if (kf.second != 0.) { + ovrFPS = agi::vfr::Framerate(kf.second); + ovrTimecodeFile.clear(); + SubtitlesChanged(); + } + config::mru->Add("Keyframes", STD_STR(filename)); } - catch (const wchar_t *error) { - wxMessageBox(error, _T("Error opening keyframes file"), wxOK | wxICON_ERROR, NULL); - } - catch (agi::acs::AcsNotFound const&) { - wxLogError(L"Could not open file " + filename); + catch (agi::keyframe::Error const& err) { + wxMessageBox(err.GetMessage(), "Error opening keyframes file", wxOK | wxICON_ERROR, NULL); config::mru->Remove("Keyframes", STD_STR(filename)); } - catch (...) { - wxMessageBox(_T("Unknown error"), _T("Error opening keyframes file"), wxOK | wxICON_ERROR, NULL); + catch (agi::acs::AcsError const&) { + wxLogError(L"Could not open file " + filename); + config::mru->Remove("Keyframes", STD_STR(filename)); } } void VideoContext::SaveKeyframes(wxString filename) { - KeyFrameFile::Save(filename, GetKeyFrames()); + agi::keyframe::Save(STD_STR(filename), GetKeyFrames(), FPS()); + config::mru->Add("Keyframes", STD_STR(filename)); } void VideoContext::CloseKeyframes() { diff --git a/aegisub/src/video_context.h b/aegisub/src/video_context.h index f8c2c31d3..9de93fbba 100644 --- a/aegisub/src/video_context.h +++ b/aegisub/src/video_context.h @@ -57,7 +57,6 @@ class SubtitlesGrid; class AudioProvider; class AudioDisplay; class AssDialogue; -class KeyFrameFile; class SubtitlesProviderErrorEvent; class ThreadedFrameSource; class VideoProvider; @@ -75,7 +74,6 @@ namespace agi { /// DOCME class VideoContext : public wxEvtHandler { friend class AudioProvider; - friend class KeyFrameFile; /// Current frame number changed (new frame number) agi::signal::Signal Seek;