Rewrite keyframe loading and saving code and move it to libaegisub

Originally committed to SVN as r5073.
This commit is contained in:
Thomas Goyne 2010-12-31 21:02:17 +00:00
parent ea93f6afba
commit edff7d6a2d
12 changed files with 186 additions and 257 deletions

View file

@ -1361,14 +1361,6 @@
<Filter
Name="Video backend"
>
<File
RelativePath="..\..\src\keyframe.cpp"
>
</File>
<File
RelativePath="..\..\src\keyframe.h"
>
</File>
<File
RelativePath="..\..\src\threaded_frame_source.cpp"
>

View file

@ -275,6 +275,10 @@
RelativePath="..\..\libaegisub\common\charset_ucd.cpp"
>
</File>
<File
RelativePath="..\..\libaegisub\common\keyframe.cpp"
>
</File>
<File
RelativePath="..\..\libaegisub\common\log.cpp"
>
@ -421,6 +425,10 @@
RelativePath="..\..\libaegisub\include\libaegisub\io.h"
>
</File>
<File
RelativePath="..\..\libaegisub\include\libaegisub\keyframe.h"
>
</File>
<File
RelativePath="..\..\libaegisub\include\libaegisub\line_iterator.h"
>

View file

@ -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 \

View file

@ -0,0 +1,117 @@
// Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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 <algorithm>
#include <fstream>
#endif
#include "libaegisub/io.h"
#include "libaegisub/line_iterator.h"
#include "libaegisub/keyframe.h"
#include "libaegisub/vfr.h"
static std::pair<std::vector<int>, 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<int> ret;
std::copy(std::istream_iterator<int>(file), std::istream_iterator<int>(), std::back_inserter(ret));
return make_pair(ret, fps);
}
static std::pair<std::vector<int>, double> other_keyframes(std::istream &file, char (*func)(std::string const&)) {
int count = 0;
std::vector<int> ret;
agi::line_iterator<std::string> end;
for (agi::line_iterator<std::string> 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<int N>
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<int> 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<int>(of, "\n"));
}
std::pair<std::vector<int>, double> Load(std::string const& filename) {
std::auto_ptr<std::ifstream> 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");
}
} }

View file

@ -0,0 +1,43 @@
// Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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 <vector>
#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<std::vector<int>, 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<int> const& keyframes, vfr::Framerate const& fps);
DEFINE_SIMPLE_EXCEPTION_NOINNER(Error, Exception, "keyframe/error")
}
}

View file

@ -194,7 +194,6 @@ SRC += \
help_button.cpp \
hotkeys.cpp \
kana_table.cpp \
keyframe.cpp \
main.cpp \
md5.c \
mkv_wrap.cpp \

View file

@ -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"

View file

@ -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"

View file

@ -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 <wx/msgdlg.h>
#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<int> KeyFrameFile::Load(wxString filename) {
std::vector<int> 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<int> 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;i<keyFrames.size();i++) {
file.WriteLineToFile(wxString::Format(_T("%i"),keyFrames[i]));
}
config::mru->Add("Keyframes", STD_STR(filename));
}
/// @brief Aegisub keyframes file
/// @param file
/// @param keyFrames
///
void KeyFrameFile::OpenAegiKeyFrames(TextFileReader& file, std::vector<int>& 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<int>& 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<int>& 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<int>& 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();
}
}

View file

@ -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<int> Load(wxString filename);
static void Save(wxString filename, std::vector<int> const& keyframes);
private:
static void OpenAegiKeyFrames(TextFileReader& file, std::vector<int>& keyFrames);
static void OpenXviDKeyFrames(TextFileReader& file, std::vector<int>& keyFrames);
static void OpenDivXKeyFrames(TextFileReader& file, std::vector<int>& keyFrames);
static void Openx264KeyFrames(TextFileReader& file, std::vector<int>& keyFrames);
};

View file

@ -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 <libaegisub/keyframe.h>
#include <libaegisub/access.h>
#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<std::vector<int>, 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() {

View file

@ -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<int> Seek;