mirror of https://github.com/odrling/Aegisub
Rewrite vfr.cpp in libaegisub with tests. Not yet used by Aegisub itself.
Originally committed to SVN as r4661.
This commit is contained in:
parent
929fa83dd9
commit
9322f95071
|
@ -226,6 +226,10 @@
|
|||
RelativePath="..\..\libaegisub\common\validator.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\libaegisub\common\vfr.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Windows"
|
||||
|
@ -344,6 +348,10 @@
|
|||
RelativePath="..\..\libaegisub\include\libaegisub\io.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\libaegisub\include\libaegisub\line_iterator.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\libaegisub\include\libaegisub\log.h"
|
||||
>
|
||||
|
@ -384,6 +392,10 @@
|
|||
RelativePath="..\..\libaegisub\include\libaegisub\validator.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\libaegisub\include\libaegisub\vfr.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<File
|
||||
RelativePath="..\..\libaegisub\lagi_pre.h"
|
||||
|
|
|
@ -86,6 +86,7 @@
|
|||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
CommandLine="cd "$(ExecutableOutDir)"
"$(ProjectDir)\..\..\tests\setup.bat" "$(ProjectDir)\..\..\tests"
"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
|
@ -248,6 +249,10 @@
|
|||
RelativePath="..\..\tests\libaegisub_util.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\tests\libaegisub_vfr.cpp"
|
||||
>
|
||||
</File>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
|
|
|
@ -30,6 +30,7 @@ libaegisub_2_2_la_SOURCES = \
|
|||
common/option_visit.cpp \
|
||||
common/log.cpp \
|
||||
common/validator.cpp \
|
||||
common/vfr.cpp \
|
||||
unix/util.cpp \
|
||||
unix/io.cpp \
|
||||
unix/access.cpp \
|
||||
|
|
|
@ -0,0 +1,307 @@
|
|||
// Copyright (c) 2010, 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 vfr.cpp
|
||||
/// @brief Framerate handling of all sorts
|
||||
/// @ingroup libaegisub video_input
|
||||
|
||||
#include "libaegisub/vfr.h"
|
||||
|
||||
#ifndef LAGI_PRE
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
#endif
|
||||
|
||||
#include "libaegisub/charset.h"
|
||||
#include "libaegisub/io.h"
|
||||
#include "libaegisub/line_iterator.h"
|
||||
|
||||
namespace std {
|
||||
template<> void swap(agi::vfr::Framerate &lft, agi::vfr::Framerate &rgt) throw() {
|
||||
lft.swap(rgt);
|
||||
}
|
||||
}
|
||||
|
||||
namespace agi {
|
||||
namespace vfr {
|
||||
|
||||
static int is_increasing(int prev, int cur) {
|
||||
if (prev >= cur) {
|
||||
throw UnorderedTimecodes("Timecodes are out of order or too close together");
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
/// @brief Verify that timecodes monotonically increase
|
||||
/// @param timecodes List of timecodes to check
|
||||
static void validate_timecodes(std::vector<int> const& timecodes) {
|
||||
if (timecodes.size() <= 1) {
|
||||
throw TooFewTimecodes("Must have at least two timecodes to do anything useful");
|
||||
}
|
||||
std::accumulate(timecodes.begin()+1, timecodes.end(), timecodes.front(), is_increasing);
|
||||
}
|
||||
|
||||
// A "start,end,fps" line in a v1 timecode file
|
||||
struct TimecodeRange {
|
||||
int start;
|
||||
int end;
|
||||
double fps;
|
||||
double time;
|
||||
bool operator<(TimecodeRange cmp) {
|
||||
return start < cmp.start;
|
||||
}
|
||||
TimecodeRange() : fps(0.) { }
|
||||
};
|
||||
|
||||
/// @brief Parse a single line of a v1 timecode file
|
||||
/// @param str Line to parse
|
||||
/// @return The line in TimecodeRange form, or TimecodeRange() if it's a comment
|
||||
static TimecodeRange v1_parse_line(std::string const& str) {
|
||||
if (str.empty() || str[0] == '#') return TimecodeRange();
|
||||
|
||||
std::istringstream ss(str);
|
||||
TimecodeRange range;
|
||||
char comma1, comma2;
|
||||
ss >> range.start >> comma1 >> range.end >> comma2 >> range.fps;
|
||||
if (ss.fail() || comma1 != ',' || comma2 != ',' || !ss.eof()) {
|
||||
throw MalformedLine(str);
|
||||
}
|
||||
if (range.start < 0 || range.end < 0) {
|
||||
throw UnorderedTimecodes("Cannot specify frame rate for negative frames.");
|
||||
}
|
||||
if (range.end < range.start) {
|
||||
throw UnorderedTimecodes("End frame must be greater than or equal to start frame");
|
||||
}
|
||||
if (range.fps <= 0.) {
|
||||
throw BadFPS("FPS must be greater than zero");
|
||||
}
|
||||
if (range.fps > 1000.) {
|
||||
// This is our limitation, not mkvmerge's
|
||||
// mkvmerge uses nanoseconds internally
|
||||
throw BadFPS("FPS must be at most 1000");
|
||||
}
|
||||
return range;
|
||||
}
|
||||
|
||||
/// @brief Is the timecode range a comment line?
|
||||
static bool v1_invalid_timecode(TimecodeRange const& range) {
|
||||
return range.fps == 0.;
|
||||
}
|
||||
|
||||
/// @brief Generate override ranges for all frames with assumed fpses
|
||||
/// @param ranges List with ranges which is mutated
|
||||
/// @param fps Assumed fps to use for gaps
|
||||
static void v1_fill_range_gaps(std::list<TimecodeRange> &ranges, double fps) {
|
||||
// Range for frames between start and first override
|
||||
if (ranges.empty() || ranges.front().start > 0) {
|
||||
TimecodeRange range;
|
||||
range.fps = fps;
|
||||
range.start = 0;
|
||||
range.end = ranges.empty() ? 0 : ranges.front().start - 1;
|
||||
ranges.push_front(range);
|
||||
}
|
||||
std::list<TimecodeRange>::iterator cur = ++ranges.begin();
|
||||
std::list<TimecodeRange>::iterator prev = ranges.begin();
|
||||
for (; cur != ranges.end(); ++cur, ++prev) {
|
||||
if (prev->end >= cur->start) {
|
||||
// mkvmerge allows overlapping timecode ranges, but does completely
|
||||
// broken things with them
|
||||
throw UnorderedTimecodes("Override ranges must not overlap");
|
||||
}
|
||||
if (prev->end + 1 < cur->start) {
|
||||
TimecodeRange range;
|
||||
range.fps = fps;
|
||||
range.start = prev->end + 1;
|
||||
range.end = cur->start - 1;
|
||||
ranges.insert(cur, range);
|
||||
++prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Parse a v1 timecode file
|
||||
/// @param file Iterator of lines in the file
|
||||
/// @param line Header of file with assumed fps
|
||||
/// @param[out] timecodes Vector filled with frame start times
|
||||
/// @param[out] time Unrounded time of the last frame
|
||||
/// @return Assumed fps
|
||||
static double v1_parse(line_iterator<std::string> file, std::string line, std::vector<int> &timecodes, double &time) {
|
||||
using namespace std;
|
||||
double fps = atof(line.substr(7).c_str());
|
||||
if (fps <= 0.) throw BadFPS("Assumed FPS must be greater than zero");
|
||||
if (fps > 1000.) throw BadFPS("Assumed FPS must not be greater than 1000");
|
||||
|
||||
list<TimecodeRange> ranges;
|
||||
transform(file, line_iterator<string>(), back_inserter(ranges), v1_parse_line);
|
||||
ranges.erase(remove_if(ranges.begin(), ranges.end(), v1_invalid_timecode), ranges.end());
|
||||
|
||||
ranges.sort();
|
||||
v1_fill_range_gaps(ranges, fps);
|
||||
timecodes.reserve(ranges.back().end);
|
||||
|
||||
time = 0.;
|
||||
for (list<TimecodeRange>::iterator cur = ranges.begin(); cur != ranges.end(); ++cur) {
|
||||
for (int frame = cur->start; frame <= cur->end; frame++) {
|
||||
timecodes.push_back(int(time + .5));
|
||||
time += 1000. / cur->fps;
|
||||
}
|
||||
}
|
||||
timecodes.push_back(int(time + .5));
|
||||
return fps;
|
||||
}
|
||||
|
||||
Framerate::Framerate(Framerate const& that)
|
||||
: fps(that.fps)
|
||||
, last(that.last)
|
||||
, timecodes(that.timecodes)
|
||||
{
|
||||
}
|
||||
|
||||
Framerate::Framerate(double fps) : fps(fps), last(0.) {
|
||||
if (fps < 0.) throw BadFPS("FPS must be greater than zero");
|
||||
if (fps > 1000.) throw BadFPS("FPS must not be greater than 1000");
|
||||
}
|
||||
|
||||
Framerate::Framerate(std::vector<int> const& timecodes)
|
||||
: timecodes(timecodes)
|
||||
{
|
||||
validate_timecodes(timecodes);
|
||||
fps = timecodes.size() / (timecodes.back() / 1000.);
|
||||
last = timecodes.back();
|
||||
}
|
||||
|
||||
Framerate::~Framerate() {
|
||||
}
|
||||
|
||||
void Framerate::swap(Framerate &right) throw() {
|
||||
std::swap(fps, right.fps);
|
||||
std::swap(last, right.last);
|
||||
std::swap(timecodes, right.timecodes);
|
||||
}
|
||||
|
||||
Framerate &Framerate::operator=(Framerate right) {
|
||||
std::swap(*this, right);
|
||||
return *this;
|
||||
}
|
||||
Framerate &Framerate::operator=(double fps) {
|
||||
return *this = Framerate(fps);
|
||||
}
|
||||
|
||||
bool Framerate::operator==(Framerate const& right) const {
|
||||
return fps == right.fps && timecodes == right.timecodes;
|
||||
}
|
||||
|
||||
Framerate::Framerate(std::string const& filename) : fps(0.) {
|
||||
using namespace std;
|
||||
auto_ptr<ifstream> file(agi::io::Open(filename));
|
||||
string encoding = agi::charset::Detect(filename);
|
||||
string line = *line_iterator<string>(*file, encoding);
|
||||
if (line == "# timecode format v2") {
|
||||
copy(line_iterator<int>(*file, encoding), line_iterator<int>(), back_inserter(timecodes));
|
||||
validate_timecodes(timecodes);
|
||||
fps = timecodes.size() / (timecodes.back() / 1000.);
|
||||
return;
|
||||
}
|
||||
if (line == "# timecode format v1" || line.substr(0, 7) == "Assume ") {
|
||||
if (line[0] == '#') {
|
||||
line = *line_iterator<string>(*file, encoding);
|
||||
}
|
||||
fps = v1_parse(line_iterator<string>(*file, encoding), line, timecodes, last);
|
||||
return;
|
||||
}
|
||||
|
||||
throw UnknownFormat(line);
|
||||
}
|
||||
|
||||
void Framerate::Save(std::string const& filename, int length) const {
|
||||
agi::io::Save file(filename);
|
||||
std::ofstream &out = file.Get();
|
||||
|
||||
out << "# timecode format v2\n";
|
||||
std::copy(timecodes.begin(), timecodes.end(), std::ostream_iterator<int>(out, "\n"));
|
||||
for (int written = timecodes.size(); written < length; ++written) {
|
||||
out << TimeAtFrame(written) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
static int round(double value) {
|
||||
return int(value + .5);
|
||||
}
|
||||
|
||||
int Framerate::FrameAtTime(int ms, Time type) const {
|
||||
// With X ms per frame, this should return 0 for:
|
||||
// EXACT: [0, X]
|
||||
// START: [-X, 0]
|
||||
// END: [1, X + 1]
|
||||
|
||||
// There are two properties we take advantage of here:
|
||||
// 1. START and END's ranges are adjacent, meaning doing the calculations
|
||||
// for END and adding one gives us START
|
||||
// 2. END is EXACT plus one ms, meaning we can subtract one ms to get EXACT
|
||||
|
||||
// Combining these allows us to easily calculate START and END in terms of
|
||||
// EXACT
|
||||
|
||||
if (type == START) {
|
||||
return FrameAtTime(ms - 1) + 1;
|
||||
}
|
||||
if (type == END) {
|
||||
return FrameAtTime(ms - 1);
|
||||
}
|
||||
|
||||
if (timecodes.empty()) {
|
||||
return (int)floor(ms * fps / 1000.);
|
||||
}
|
||||
if (ms < timecodes.front()) {
|
||||
return (int)floor((ms - timecodes.front()) * fps / 1000.);
|
||||
}
|
||||
if (ms > timecodes.back()) {
|
||||
return round((ms - timecodes.back()) * fps / 1000.) + timecodes.size() - 1;
|
||||
}
|
||||
|
||||
return std::distance(std::lower_bound(timecodes.rbegin(), timecodes.rend(), ms, std::greater<int>()), timecodes.rend()) - 1;
|
||||
}
|
||||
|
||||
int Framerate::TimeAtFrame(int frame, Time type) const {
|
||||
if (type == START) {
|
||||
int prev = TimeAtFrame(frame - 1);
|
||||
int cur = TimeAtFrame(frame);
|
||||
// + 1 as these need to round up for the case of two frames 1 ms apart
|
||||
return prev + (cur - prev + 1) / 2;
|
||||
}
|
||||
if (type == END) {
|
||||
int cur = TimeAtFrame(frame);
|
||||
int next = TimeAtFrame(frame + 1);
|
||||
return cur + (next - cur + 1) / 2;
|
||||
}
|
||||
|
||||
if (timecodes.empty()) {
|
||||
return (int)ceil(frame / fps * 1000.);
|
||||
}
|
||||
|
||||
if (frame < 0) {
|
||||
return (int)ceil(frame / fps * 1000.) + timecodes.front();
|
||||
}
|
||||
if (frame >= (signed)timecodes.size()) {
|
||||
return round((frame - timecodes.size() + 1) * 1000. / fps + last);
|
||||
}
|
||||
return timecodes[frame];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -38,7 +38,7 @@ namespace agi {
|
|||
|
||||
/// @class line_iterator
|
||||
/// @brief An iterator over lines in a stream
|
||||
template<typename OutputType = std::string>
|
||||
template<class OutputType = std::string>
|
||||
class line_iterator : public std::iterator<std::input_iterator_tag, OutputType> {
|
||||
std::istream *stream; ///< Stream to iterator over
|
||||
bool valid; ///< Are there any more values to read?
|
||||
|
@ -151,7 +151,7 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
template<typename OutputType>
|
||||
template<class OutputType>
|
||||
void line_iterator<OutputType>::getline(std::string &str) {
|
||||
union {
|
||||
int32_t chr;
|
||||
|
@ -180,7 +180,7 @@ void line_iterator<OutputType>::getline(std::string &str) {
|
|||
}
|
||||
}
|
||||
|
||||
template<typename OutputType>
|
||||
template<class OutputType>
|
||||
void line_iterator<OutputType>::next() {
|
||||
if (!valid) return;
|
||||
if (!stream->good()) {
|
||||
|
@ -198,7 +198,7 @@ void line_iterator<OutputType>::next() {
|
|||
}
|
||||
}
|
||||
|
||||
template<typename OutputType>
|
||||
template<class OutputType>
|
||||
inline bool line_iterator<OutputType>::convert(std::string &str) {
|
||||
std::istringstream ss(str);
|
||||
ss >> value;
|
||||
|
@ -210,7 +210,7 @@ inline bool line_iterator<std::string>::convert(std::string &str) {
|
|||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<class T>
|
||||
void swap(agi::line_iterator<T> &lft, agi::line_iterator<T> &rgt) {
|
||||
lft.swap(rgt);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
// Copyright (c) 2010, 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 vfr.h
|
||||
/// @brief Framerate handling of all sorts
|
||||
/// @ingroup libaegisub video_input
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(AGI_PRE) && !defined(LAGI_PRE)
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#endif
|
||||
|
||||
#include <libaegisub/exception.h>
|
||||
|
||||
namespace agi {
|
||||
namespace vfr {
|
||||
|
||||
enum Time {
|
||||
/// Use the actual frame times
|
||||
/// With 1 FPS video, frame 0 is [0, 999] ms
|
||||
EXACT,
|
||||
/// Calculate based on the rules for start times of lines
|
||||
/// Lines are first visible on the first frame with start time less than
|
||||
/// or equal to the start time; thus with 1.0 FPS video, frame 0 is
|
||||
/// [-999, 0] ms
|
||||
START,
|
||||
/// Calculate based on the rules for end times of lines
|
||||
/// Lines are last visible on the last frame with start time less than the
|
||||
/// end time; thus with 1.0 FPS video, frame 0 is [1, 1000] ms
|
||||
END
|
||||
};
|
||||
|
||||
DEFINE_BASE_EXCEPTION_NOINNER(Error, Exception)
|
||||
/// FPS specified is not a valid frame rate
|
||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(BadFPS, Error, "vfr/badfps")
|
||||
/// Unknown timecode file format
|
||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(UnknownFormat, Error, "vfr/timecodes/unknownformat")
|
||||
/// Invalid line encountered in a timecode file
|
||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(MalformedLine, Error, "vfr/timecodes/malformed")
|
||||
/// Timecode file or vector has too few timecodes to be usable
|
||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(TooFewTimecodes, Error, "vfr/timecodes/toofew")
|
||||
/// Timecode file or vector has timecodes that are not monotonically increasing
|
||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(UnorderedTimecodes, Error, "vfr/timecodes/order")
|
||||
|
||||
/// @class Framerate
|
||||
/// @brief Class for managing everything related to converting frames to times
|
||||
/// or vice versa
|
||||
class Framerate {
|
||||
/// Average FPS for v2, assumed FPS for v1, fps for CFR
|
||||
double fps;
|
||||
/// Unrounded time of the last frame in a v1 override range. Needed to
|
||||
/// match mkvmerge's rounding
|
||||
double last;
|
||||
/// Start time in milliseconds of each frame
|
||||
std::vector<int> timecodes;
|
||||
public:
|
||||
/// Copy constructor
|
||||
Framerate(Framerate const&);
|
||||
/// @brief VFR from timecodes file
|
||||
/// @param filename File with v1 or v2 timecodes
|
||||
///
|
||||
/// Note that loading a v1 timecode file with Assume X and no overrides is
|
||||
/// not the same thing as CFR X. When timecodes are loaded from a file,
|
||||
/// mkvmerge-style rounding is applied, while setting a constant frame rate
|
||||
/// uses truncation.
|
||||
Framerate(std::string const& filename);
|
||||
/// @brief CFR constructor
|
||||
/// @param fps Frames per second or 0 for unloaded
|
||||
Framerate(double fps = 0.);
|
||||
/// @brief VFR from frame times
|
||||
/// @param timecodes Vector of frame start times in milliseconds
|
||||
Framerate(std::vector<int> const& timecodes);
|
||||
~Framerate();
|
||||
/// Atomic assignment operator
|
||||
Framerate &operator=(Framerate);
|
||||
/// Atomic CFR assignment operator
|
||||
Framerate &operator=(double);
|
||||
/// Helper function for the std::swap specialization
|
||||
void swap(Framerate &right) throw();
|
||||
|
||||
/// @brief Get the frame visible at a given time
|
||||
/// @param ms Time in milliseconds
|
||||
/// @param type Time mode
|
||||
///
|
||||
/// When type is EXACT, the frame returned is the frame visible at the given
|
||||
/// time; when it is START or END it is the frame on which a line with that
|
||||
/// start/end time would first/last be visible
|
||||
int FrameAtTime(int ms, Time type = EXACT) const;
|
||||
|
||||
/// @brief Get the time at a given frame
|
||||
/// @param frame Frame number
|
||||
/// @param type Time mode
|
||||
///
|
||||
/// When type is EXACT, the frame's exact start time is returned; START and
|
||||
/// END give a time somewhere within the range that will result in the line
|
||||
/// starting/ending on that frame
|
||||
///
|
||||
/// With v2 timecodes, frames outside the defined range are not an error
|
||||
/// and are guaranteed to be monotonically increasing/decreasing values
|
||||
/// which when passed to FrameAtTime will return the original frame; they
|
||||
/// are not guaranteed to be sensible or useful for any other purpose
|
||||
///
|
||||
/// v1 timecodes and CFR do not have a defined range, and will give sensible
|
||||
/// results for all frame numbers
|
||||
int TimeAtFrame(int frame, Time type = EXACT) const;
|
||||
|
||||
/// @brief Save the current time codes to a file as v2 timecodes
|
||||
/// @param file File name
|
||||
/// @param length Minimum number of frames to output
|
||||
///
|
||||
/// The length parameter is only particularly useful for v1 timecodes (and
|
||||
/// CFR, but saving CFR timecodes is a bit silly). Extra timecodes generated
|
||||
/// to hit length with v2 timecodes will monotonically increase but may not
|
||||
/// be otherwise sensible.
|
||||
void Save(std::string const& file, int length = -1) const;
|
||||
|
||||
bool IsVFR() const {return !timecodes.empty(); }
|
||||
bool IsLoaded() const { return !timecodes.empty() || fps; };
|
||||
double FPS() const { return fps; }
|
||||
|
||||
/// @brief Equality operator
|
||||
/// @attention O(n) when both arguments are VFR
|
||||
bool operator==(Framerate const& right) const;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -27,6 +27,8 @@
|
|||
#include <deque>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
#include <map>
|
||||
#ifdef _WIN32
|
||||
#include <memory>
|
||||
|
|
|
@ -15,7 +15,8 @@ run_SOURCES = \
|
|||
libaegisub_iconv.cpp \
|
||||
libaegisub_line_iterator.cpp \
|
||||
libaegisub_mru.cpp \
|
||||
libaegisub_util.cpp
|
||||
libaegisub_util.cpp \
|
||||
libaegisub_vfr.cpp
|
||||
|
||||
run_SOURCES += \
|
||||
*.h
|
||||
|
|
|
@ -0,0 +1,378 @@
|
|||
// Copyright (c) 2010, 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 libaegisub_vfr.cpp
|
||||
/// @brief agi::vfr::Framerate tests
|
||||
/// @ingroup video_input
|
||||
|
||||
#include <libaegisub/vfr.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
|
||||
#include "main.h"
|
||||
#include "util.h"
|
||||
|
||||
using namespace agi::vfr;
|
||||
using namespace util;
|
||||
|
||||
#define EXPECT_RANGE(low, high, test) \
|
||||
EXPECT_LE(low, test); \
|
||||
EXPECT_GE(high, test)
|
||||
|
||||
TEST(lagi_vfr, constructors_good) {
|
||||
EXPECT_NO_THROW(Framerate(1.));
|
||||
EXPECT_NO_THROW(Framerate(Framerate(1.)));
|
||||
EXPECT_NO_THROW(Framerate(make_vector<int>(2, 0, 10)));
|
||||
|
||||
EXPECT_NO_THROW(Framerate("data/vfr/in/v1_start_equals_end.txt"));
|
||||
EXPECT_NO_THROW(Framerate("data/vfr/in/v1_whitespace.txt"));
|
||||
EXPECT_NO_THROW(Framerate("data/vfr/in/v1_assume_int.txt"));
|
||||
EXPECT_NO_THROW(Framerate("data/vfr/in/v1_out_of_order.txt"));
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, constructors_bad_cfr) {
|
||||
EXPECT_THROW(Framerate(-1.), BadFPS);
|
||||
EXPECT_THROW(Framerate(1000.1), BadFPS);
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, constructors_bad_timecodes) {
|
||||
EXPECT_THROW(Framerate(make_vector<int>(0)), TooFewTimecodes);
|
||||
EXPECT_THROW(Framerate(make_vector<int>(1, 0)), TooFewTimecodes);
|
||||
EXPECT_THROW(Framerate(make_vector<int>(2, 10, 0)), UnorderedTimecodes);
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, constructors_bad_v1) {
|
||||
EXPECT_THROW(Framerate("data/vfr/in/v1_bad_seperators.txt"), MalformedLine);
|
||||
EXPECT_THROW(Framerate("data/vfr/in/v1_too_few_parts.txt"), MalformedLine);
|
||||
EXPECT_THROW(Framerate("data/vfr/in/v1_too_many_parts.txt"), MalformedLine);
|
||||
EXPECT_THROW(Framerate("data/vfr/in/v1_float_frame_number.txt"), MalformedLine);
|
||||
EXPECT_THROW(Framerate("data/vfr/in/v1_start_end_overlap.txt"), UnorderedTimecodes);
|
||||
EXPECT_THROW(Framerate("data/vfr/in/v1_fully_contained.txt"), UnorderedTimecodes);
|
||||
EXPECT_THROW(Framerate("data/vfr/in/v1_assume_over_1000.txt"), BadFPS);
|
||||
EXPECT_THROW(Framerate("data/vfr/in/v1_override_over_1000.txt"), BadFPS);
|
||||
EXPECT_THROW(Framerate("data/vfr/in/v1_override_zero.txt"), BadFPS);
|
||||
EXPECT_THROW(Framerate("data/vfr/in/v1_negative_start_of_range.txt"), UnorderedTimecodes);
|
||||
EXPECT_THROW(Framerate("data/vfr/in/v1_end_less_than_start.txt"), UnorderedTimecodes);
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, constructors_bad_v2) {
|
||||
EXPECT_THROW(Framerate("data/vfr/in/v2_empty.txt"), TooFewTimecodes);
|
||||
EXPECT_THROW(Framerate("data/vfr/in/v2_out_of_order.txt"), UnorderedTimecodes);
|
||||
|
||||
EXPECT_THROW(Framerate("data/vfr/in/empty.txt"), UnknownFormat);
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, cfr_frame_at_time_exact) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate(1.));
|
||||
EXPECT_EQ(0, fps.FrameAtTime(0));
|
||||
EXPECT_EQ(0, fps.FrameAtTime(999));
|
||||
EXPECT_EQ(1, fps.FrameAtTime(1000));
|
||||
EXPECT_EQ(1, fps.FrameAtTime(1999));
|
||||
EXPECT_EQ(100, fps.FrameAtTime(100000));
|
||||
EXPECT_EQ(-1, fps.FrameAtTime(-1));
|
||||
EXPECT_EQ(-1, fps.FrameAtTime(-1000));
|
||||
EXPECT_EQ(-2, fps.FrameAtTime(-1001));
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, cfr_frame_at_time_start) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate(1.));
|
||||
EXPECT_EQ(0, fps.FrameAtTime(0, START));
|
||||
EXPECT_EQ(1, fps.FrameAtTime(1, START));
|
||||
EXPECT_EQ(1, fps.FrameAtTime(1000, START));
|
||||
EXPECT_EQ(2, fps.FrameAtTime(1001, START));
|
||||
EXPECT_EQ(100, fps.FrameAtTime(100000, START));
|
||||
EXPECT_EQ(0, fps.FrameAtTime(-1, START));
|
||||
EXPECT_EQ(0, fps.FrameAtTime(-999, START));
|
||||
EXPECT_EQ(-1, fps.FrameAtTime(-1000, START));
|
||||
EXPECT_EQ(-1, fps.FrameAtTime(-1999, START));
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, cfr_frame_at_time_end) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate(1.));
|
||||
EXPECT_EQ(-1, fps.FrameAtTime(0, END));
|
||||
EXPECT_EQ(0, fps.FrameAtTime(1, END));
|
||||
EXPECT_EQ(0, fps.FrameAtTime(1000, END));
|
||||
EXPECT_EQ(1, fps.FrameAtTime(1001, END));
|
||||
EXPECT_EQ(99, fps.FrameAtTime(100000, END));
|
||||
EXPECT_EQ(-1, fps.FrameAtTime(-1, END));
|
||||
EXPECT_EQ(-1, fps.FrameAtTime(-999, END));
|
||||
EXPECT_EQ(-2, fps.FrameAtTime(-1000, END));
|
||||
EXPECT_EQ(-2, fps.FrameAtTime(-1999, END));
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, cfr_time_at_frame_exact) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate(1.));
|
||||
EXPECT_EQ( 0, fps.TimeAtFrame(0));
|
||||
EXPECT_EQ( 1000, fps.TimeAtFrame(1));
|
||||
EXPECT_EQ( 2000, fps.TimeAtFrame(2));
|
||||
EXPECT_EQ(-1000, fps.TimeAtFrame(-1));
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, cfr_time_at_frame_start) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate(1.));
|
||||
EXPECT_RANGE( -999, 0, fps.TimeAtFrame( 0, START));
|
||||
EXPECT_RANGE( 1, 1000, fps.TimeAtFrame( 1, START));
|
||||
EXPECT_RANGE( 1001, 2000, fps.TimeAtFrame( 2, START));
|
||||
EXPECT_RANGE(-1999, -1000, fps.TimeAtFrame(-1, START));
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, cfr_time_at_frame_end) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate(1.));
|
||||
EXPECT_RANGE( 1, 1000, fps.TimeAtFrame( 0, END));
|
||||
EXPECT_RANGE(1001, 2000, fps.TimeAtFrame( 1, END));
|
||||
EXPECT_RANGE(2001, 3000, fps.TimeAtFrame( 2, END));
|
||||
EXPECT_RANGE(-999, 0, fps.TimeAtFrame(-1, END));
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, cfr_round_trip_exact) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate(1.));
|
||||
for (int i = -10; i < 11; i++) {
|
||||
EXPECT_EQ(i, fps.FrameAtTime(fps.TimeAtFrame(i)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, cfr_round_trip_start) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate(1.));
|
||||
for (int i = -10; i < 11; i++) {
|
||||
EXPECT_EQ(i, fps.FrameAtTime(fps.TimeAtFrame(i, START), START));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, cfr_round_trip_end) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate(1.));
|
||||
for (int i = -10; i < 11; i++) {
|
||||
EXPECT_EQ(i, fps.FrameAtTime(fps.TimeAtFrame(i, END), END));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, vfr_round_trip_exact) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate(make_vector<int>(7, 0, 1000, 1500, 2000, 2001, 2002, 2003)));
|
||||
for (int i = -10; i < 11; i++) {
|
||||
EXPECT_EQ(i, fps.FrameAtTime(fps.TimeAtFrame(i)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, vfr_round_trip_start) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate(make_vector<int>(7, 0, 1000, 1500, 2000, 2001, 2002, 2003)));
|
||||
for (int i = -10; i < 11; i++) {
|
||||
EXPECT_EQ(i, fps.FrameAtTime(fps.TimeAtFrame(i, START), START));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, vfr_round_trip_end) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate(make_vector<int>(7, 0, 1000, 1500, 2000, 2001, 2002, 2003)));
|
||||
for (int i = -10; i < 11; i++) {
|
||||
EXPECT_EQ(i, fps.FrameAtTime(fps.TimeAtFrame(i, END), END));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, vfr_time_at_frame_exact) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate(make_vector<int>(5, 0, 1000, 1500, 2000, 2001)));
|
||||
EXPECT_EQ(0, fps.TimeAtFrame(0));
|
||||
EXPECT_EQ(1000, fps.TimeAtFrame(1));
|
||||
EXPECT_EQ(1500, fps.TimeAtFrame(2));
|
||||
EXPECT_EQ(2000, fps.TimeAtFrame(3));
|
||||
EXPECT_EQ(2001, fps.TimeAtFrame(4));
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, vfr_time_at_frame_start) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate(make_vector<int>(7, 0, 1000, 1500, 2000, 2001, 2002, 2003)));
|
||||
EXPECT_GE(0, fps.TimeAtFrame(0, START));
|
||||
EXPECT_RANGE(1, 1000, fps.TimeAtFrame(1, START));
|
||||
EXPECT_RANGE(1001, 1500, fps.TimeAtFrame(2, START));
|
||||
EXPECT_RANGE(1501, 2000, fps.TimeAtFrame(3, START));
|
||||
EXPECT_EQ(2001, fps.TimeAtFrame(4, START));
|
||||
EXPECT_EQ(2002, fps.TimeAtFrame(5, START));
|
||||
EXPECT_LE(2003, fps.TimeAtFrame(6, START));
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, vfr_time_at_frame_end) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate(make_vector<int>(7, 0, 1000, 1500, 2000, 2001, 2002, 2003)));
|
||||
EXPECT_RANGE(1, 1000, fps.TimeAtFrame(0, END));
|
||||
EXPECT_RANGE(1001, 1500, fps.TimeAtFrame(1, END));
|
||||
EXPECT_RANGE(1501, 2000, fps.TimeAtFrame(2, END));
|
||||
EXPECT_EQ(2001, fps.TimeAtFrame(3, END));
|
||||
EXPECT_EQ(2002, fps.TimeAtFrame(4, END));
|
||||
EXPECT_EQ(2003, fps.TimeAtFrame(5, END));
|
||||
EXPECT_LE(2004, fps.TimeAtFrame(6, END));
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, vfr_time_at_frame_outside_range) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate(make_vector<int>(3, 0, 100, 200)));
|
||||
EXPECT_GT(0, fps.TimeAtFrame(-1));
|
||||
EXPECT_EQ(0, fps.TimeAtFrame(0));
|
||||
EXPECT_EQ(100, fps.TimeAtFrame(1));
|
||||
EXPECT_EQ(200, fps.TimeAtFrame(2));
|
||||
EXPECT_LT(200, fps.TimeAtFrame(3));
|
||||
|
||||
int prev = fps.TimeAtFrame(3);
|
||||
for (int i = 4; i < 10; i++) {
|
||||
EXPECT_LT(prev, fps.TimeAtFrame(i));
|
||||
prev = fps.TimeAtFrame(i);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, vfr_frame_at_time_exact) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate(make_vector<int>(7, 0, 1000, 1500, 2000, 2001, 2002, 2003)));
|
||||
EXPECT_GT(0, fps.FrameAtTime(-1));
|
||||
EXPECT_EQ(0, fps.FrameAtTime(0));
|
||||
EXPECT_EQ(0, fps.FrameAtTime(999));
|
||||
EXPECT_EQ(1, fps.FrameAtTime(1000));
|
||||
EXPECT_EQ(1, fps.FrameAtTime(1499));
|
||||
EXPECT_EQ(2, fps.FrameAtTime(1500));
|
||||
EXPECT_EQ(2, fps.FrameAtTime(1999));
|
||||
EXPECT_EQ(3, fps.FrameAtTime(2000));
|
||||
EXPECT_EQ(4, fps.FrameAtTime(2001));
|
||||
EXPECT_EQ(5, fps.FrameAtTime(2002));
|
||||
EXPECT_EQ(6, fps.FrameAtTime(2003));
|
||||
EXPECT_LE(6, fps.FrameAtTime(2004));
|
||||
}
|
||||
TEST(lagi_vfr, vfr_frame_at_time_start) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate(make_vector<int>(7, 0, 1000, 1500, 2000, 2001, 2002, 2003)));
|
||||
EXPECT_GE(0, fps.FrameAtTime(-1, START));
|
||||
EXPECT_EQ(0, fps.FrameAtTime(0, START));
|
||||
EXPECT_EQ(1, fps.FrameAtTime(1, START));
|
||||
EXPECT_EQ(1, fps.FrameAtTime(1000, START));
|
||||
EXPECT_EQ(2, fps.FrameAtTime(1001, START));
|
||||
EXPECT_EQ(2, fps.FrameAtTime(1500, START));
|
||||
EXPECT_EQ(3, fps.FrameAtTime(1501, START));
|
||||
EXPECT_EQ(3, fps.FrameAtTime(2000, START));
|
||||
EXPECT_EQ(4, fps.FrameAtTime(2001, START));
|
||||
EXPECT_EQ(5, fps.FrameAtTime(2002, START));
|
||||
EXPECT_EQ(6, fps.FrameAtTime(2003, START));
|
||||
EXPECT_LE(6, fps.FrameAtTime(2004, START));
|
||||
}
|
||||
|
||||
bool validate_save(std::string const& goodFilename, std::string const& checkFilename, int v2Lines = -1, bool allowLonger = false) {
|
||||
std::ifstream good(goodFilename.c_str());
|
||||
std::ifstream check(checkFilename.c_str());
|
||||
|
||||
EXPECT_TRUE(good.good());
|
||||
EXPECT_TRUE(check.good());
|
||||
|
||||
std::string good_header;
|
||||
std::string check_header;
|
||||
|
||||
std::getline(good, good_header);
|
||||
std::getline(check, check_header);
|
||||
|
||||
// istream_iterator rather than line_reader because we never write comments
|
||||
// or empty lines in timecode files
|
||||
std::istream_iterator<double> good_iter(good);
|
||||
std::istream_iterator<double> check_iter(check);
|
||||
std::istream_iterator<double> end;
|
||||
|
||||
int line = 0;
|
||||
for (; good_iter != end; ++good_iter, ++check_iter, ++line) {
|
||||
if (check_iter == end) return false;
|
||||
if (v2Lines < 0 || line < v2Lines) {
|
||||
if (*good_iter != *check_iter) return false;
|
||||
}
|
||||
}
|
||||
// v1 timecodes with the end of a range past the end of the video are valid,
|
||||
// and when saving those there will be too many timecodes in the v2 file
|
||||
if (!allowLonger && check_iter != end) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, validate_save) {
|
||||
EXPECT_TRUE(validate_save("data/vfr/in/validate_base.txt", "data/vfr/in/validate_base.txt"));
|
||||
EXPECT_FALSE(validate_save("data/vfr/in/validate_base.txt", "data/vfr/in/validate_different.txt"));
|
||||
EXPECT_FALSE(validate_save("data/vfr/in/validate_base.txt", "data/vfr/in/validate_shorter.txt"));
|
||||
EXPECT_FALSE(validate_save("data/vfr/in/validate_base.txt", "data/vfr/in/validate_longer.txt"));
|
||||
EXPECT_TRUE(validate_save("data/vfr/in/validate_base.txt", "data/vfr/in/validate_longer.txt", -1, true));
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, save_vfr_nolen) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate(make_vector<int>(3, 0, 100, 200)));
|
||||
ASSERT_NO_THROW(fps.Save("data/vfr/out/v2_nolen.txt"));
|
||||
|
||||
EXPECT_TRUE(validate_save("data/vfr/in/v2_nolen.txt", "data/vfr/out/v2_nolen.txt"));
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, save_vfr_len) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate(make_vector<int>(3, 0, 100, 200)));
|
||||
ASSERT_NO_THROW(fps.Save("data/vfr/out/v2_len_3_10.txt", 10));
|
||||
|
||||
EXPECT_TRUE(validate_save("data/vfr/in/v2_len_3_10.txt", "data/vfr/out/v2_len_3_10.txt", 3));
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, load_v2) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate("data/vfr/in/v2_1fps.txt"));
|
||||
for (int i = 0; i < 9; i++) {
|
||||
EXPECT_EQ(i * 1000, fps.TimeAtFrame(i));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, load_v2_comments) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate("data/vfr/in/v2_comments.txt"));
|
||||
for (int i = 0; i < 9; i++) {
|
||||
EXPECT_EQ(i * 1000, fps.TimeAtFrame(i));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, load_v2_number_in_comment) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate("data/vfr/in/v2_number_in_comment.txt"));
|
||||
for (int i = 0; i < 9; i++) {
|
||||
EXPECT_EQ(i * 1000, fps.TimeAtFrame(i));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, load_v1_save_v2) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate("data/vfr/in/v1_mode5.txt"));
|
||||
EXPECT_NO_THROW(fps.Save("data/vfr/out/v2_mode5.txt"));
|
||||
|
||||
EXPECT_TRUE(validate_save("data/vfr/in/v2_mode5.txt", "data/vfr/out/v2_mode5.txt", -1, true));
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, load_v1_save_v2_len) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate("data/vfr/in/v1_assume_30.txt"));
|
||||
ASSERT_NO_THROW(fps.Save("data/vfr/out/v2_100_frames_30_fps.txt", 100));
|
||||
EXPECT_TRUE(validate_save("data/vfr/in/v2_100_frames_30_fps.txt", "data/vfr/out/v2_100_frames_30_fps.txt"));
|
||||
}
|
||||
|
||||
TEST(lagi_vfr, load_v1_save_v2_ovr) {
|
||||
Framerate fps;
|
||||
ASSERT_NO_THROW(fps = Framerate("data/vfr/in/v1_assume_30_with_override.txt"));
|
||||
ASSERT_NO_THROW(fps.Save("data/vfr/out/v2_100_frames_30_with_override.txt", 100));
|
||||
EXPECT_TRUE(validate_save("data/vfr/in/v2_100_frames_30_with_override.txt", "data/vfr/out/v2_100_frames_30_with_override.txt"));
|
||||
}
|
|
@ -38,3 +38,9 @@ echo {"String" : [{"string" : "This is a test"}, {"string" : "This is a test"}]}
|
|||
echo {"Integer" : [{"int" : 1}, {"int" : 1}]} > data/option_array_integer
|
||||
echo {"Double" : [{"double" : 2.1}, {"double" : 2.1}]} > data/option_array_double
|
||||
echo {"Bool" : [{"bool" : true}, {"bool" : true}]} > data/option_array_bool
|
||||
|
||||
mkdir data\vfr
|
||||
mkdir data\vfr\in
|
||||
mkdir data\vfr\out
|
||||
|
||||
xcopy %1\vfr data\vfr\in
|
||||
|
|
|
@ -38,3 +38,8 @@ echo '{"String" : [{"string" : "This is a test"}, {"string" : "This is a test"}]
|
|||
echo '{"Integer" : [{"int" : 1}, {"int" : 1}]}' > data/option_array_integer
|
||||
echo '{"Double" : [{"double" : 2.1}, {"double" : 2.1}]}' > data/option_array_double
|
||||
echo '{"Bool" : [{"bool" : true}, {"bool" : true}]}' > data/option_array_bool
|
||||
|
||||
mkdir data/vfr
|
||||
mkdir data/vfr/in
|
||||
mkdir data/vfr/out
|
||||
cp `dirname $0`/vfr/* data/vfr/in/
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
# timecode format v1
|
||||
Assume 30
|
|
@ -0,0 +1,3 @@
|
|||
# timecode format v1
|
||||
Assume 30
|
||||
20,50,19
|
|
@ -0,0 +1,3 @@
|
|||
# timecode format v1
|
||||
Assume 25
|
||||
0,10,30
|
|
@ -0,0 +1,2 @@
|
|||
# timecode format v1
|
||||
Assume 1000.1
|
|
@ -0,0 +1,3 @@
|
|||
# timecode format v1
|
||||
Assume 29.970030
|
||||
0;20;24.2
|
|
@ -0,0 +1,3 @@
|
|||
# timecode format v1
|
||||
Assume 29.970030
|
||||
20,0,25
|
|
@ -0,0 +1,3 @@
|
|||
# timecode format v1
|
||||
Assume 29.970030
|
||||
0.0,10.0,25
|
|
@ -0,0 +1,4 @@
|
|||
# timecode format v1
|
||||
Assume 29.970030
|
||||
0,50,10
|
||||
10,40,10
|
|
@ -0,0 +1,255 @@
|
|||
# timecode format v1
|
||||
Assume 29.970030
|
||||
# TDecimate v1.0.3 by tritical
|
||||
# Mode 5 - Auto-generated mkv timecodes file
|
||||
10,93,23.976024
|
||||
99,146,23.976024
|
||||
157,164,23.976024
|
||||
175,178,23.976024
|
||||
219,238,23.976024
|
||||
329,340,23.976024
|
||||
401,424,23.976024
|
||||
455,466,23.976024
|
||||
532,543,23.976024
|
||||
569,584,23.976024
|
||||
605,616,23.976024
|
||||
647,658,23.976024
|
||||
664,683,23.976024
|
||||
714,721,23.976024
|
||||
747,762,23.976024
|
||||
768,775,23.976024
|
||||
801,808,23.976024
|
||||
824,827,23.976024
|
||||
883,890,23.976024
|
||||
926,945,23.976024
|
||||
951,962,23.976024
|
||||
1013,1028,23.976024
|
||||
1114,1129,23.976024
|
||||
1145,1156,23.976024
|
||||
1182,1205,23.976024
|
||||
1256,1271,23.976024
|
||||
1292,1307,23.976024
|
||||
1318,1321,23.976024
|
||||
1362,1381,23.976024
|
||||
1402,1409,23.976024
|
||||
1455,1470,23.976024
|
||||
1491,1494,23.976024
|
||||
1520,1531,23.976024
|
||||
1572,1579,23.976024
|
||||
1605,1608,23.976024
|
||||
1619,1654,23.976024
|
||||
3110,3141,23.976024
|
||||
3177,3208,23.976024
|
||||
3214,3216,17.982018
|
||||
3547,3554,23.976024
|
||||
3560,3563,23.976024
|
||||
3569,3571,17.982018
|
||||
3627,3634,23.976024
|
||||
3645,3656,23.976024
|
||||
3662,3673,23.976024
|
||||
3684,3763,23.976024
|
||||
3804,3807,23.976024
|
||||
3818,3825,23.976024
|
||||
4231,4398,23.976024
|
||||
4409,4436,23.976024
|
||||
4502,4569,23.976024
|
||||
4625,4860,23.976024
|
||||
4931,5394,23.976024
|
||||
5405,5532,23.976024
|
||||
5573,5608,23.976024
|
||||
5619,5670,23.976024
|
||||
5746,5853,23.976024
|
||||
5899,5958,23.976024
|
||||
5974,6529,23.976024
|
||||
6555,6658,23.976024
|
||||
6669,6752,23.976024
|
||||
6758,6809,23.976024
|
||||
6860,6887,23.976024
|
||||
6898,7257,23.976024
|
||||
7313,7324,23.976024
|
||||
7330,7801,23.976024
|
||||
7817,7820,23.976024
|
||||
7836,7839,23.976024
|
||||
7855,7858,23.976024
|
||||
7874,7897,23.976024
|
||||
7993,8168,23.976024
|
||||
8304,8911,23.976024
|
||||
8917,9148,23.976024
|
||||
9219,9254,23.976024
|
||||
9275,9334,23.976024
|
||||
9365,9412,23.976024
|
||||
9418,9445,23.976024
|
||||
9481,9552,23.976024
|
||||
9558,9569,23.976024
|
||||
9575,9602,23.976024
|
||||
9608,9703,23.976024
|
||||
9779,9822,23.976024
|
||||
9843,10010,23.976024
|
||||
10016,10027,23.976024
|
||||
10073,10076,23.976024
|
||||
10087,10090,23.976024
|
||||
10101,10140,23.976024
|
||||
10156,10159,23.976024
|
||||
10175,10386,23.976024
|
||||
10397,10420,23.976024
|
||||
10431,10698,23.976024
|
||||
10724,10843,23.976024
|
||||
10864,11079,23.976024
|
||||
11085,11260,23.976024
|
||||
11281,11456,23.976024
|
||||
11477,11916,23.976024
|
||||
12012,12547,23.976024
|
||||
12578,12581,23.976024
|
||||
12592,12795,23.976024
|
||||
12806,13177,23.976024
|
||||
13193,13196,23.976024
|
||||
13317,13432,23.976024
|
||||
13463,13538,23.976024
|
||||
13614,13685,23.976024
|
||||
13751,13938,23.976024
|
||||
13954,13957,23.976024
|
||||
13973,13976,23.976024
|
||||
13992,13995,23.976024
|
||||
14011,14114,23.976024
|
||||
14200,14387,23.976024
|
||||
14803,14894,23.976024
|
||||
14950,14953,23.976024
|
||||
15074,15089,23.976024
|
||||
15130,15145,23.976024
|
||||
15196,15207,23.976024
|
||||
15638,15681,23.976024
|
||||
15782,15917,23.976024
|
||||
16003,16010,23.976024
|
||||
16016,16023,23.976024
|
||||
16064,16223,23.976024
|
||||
16439,16574,23.976024
|
||||
16815,16818,23.976024
|
||||
16824,17091,23.976024
|
||||
17282,17385,23.976024
|
||||
17461,17488,23.976024
|
||||
17494,17545,23.976024
|
||||
17746,17937,23.976024
|
||||
17958,18193,23.976024
|
||||
18264,18375,23.976024
|
||||
18386,18401,23.976024
|
||||
18412,18463,23.976024
|
||||
18529,18572,23.976024
|
||||
18633,18836,23.976024
|
||||
18902,19049,23.976024
|
||||
19055,19062,23.976024
|
||||
19068,19115,23.976024
|
||||
19156,19611,23.976024
|
||||
19772,19779,23.976024
|
||||
19790,19905,23.976024
|
||||
19971,20070,23.976024
|
||||
20076,20103,23.976024
|
||||
20119,20134,23.976024
|
||||
20140,20211,23.976024
|
||||
20217,20408,23.976024
|
||||
20424,21271,23.976024
|
||||
21367,21478,23.976024
|
||||
21534,21761,23.976024
|
||||
21847,21870,23.976024
|
||||
21876,22363,23.976024
|
||||
22524,22675,23.976024
|
||||
22686,22741,23.976024
|
||||
22817,22832,23.976024
|
||||
22883,22890,23.976024
|
||||
22911,23334,23.976024
|
||||
23500,23599,23.976024
|
||||
23610,23949,23.976024
|
||||
23955,24018,23.976024
|
||||
24024,24131,23.976024
|
||||
24192,24227,23.976024
|
||||
24473,24616,23.976024
|
||||
24767,25078,23.976024
|
||||
25124,25403,23.976024
|
||||
25409,25536,23.976024
|
||||
25612,25899,23.976024
|
||||
25945,25992,23.976024
|
||||
26043,26166,23.976024
|
||||
26547,26790,23.976024
|
||||
27036,27099,23.976024
|
||||
27105,27120,23.976024
|
||||
27166,27169,23.976024
|
||||
27175,27182,23.976024
|
||||
27188,27191,23.976024
|
||||
27202,27517,23.976024
|
||||
27568,27779,23.976024
|
||||
27795,27802,23.976024
|
||||
27823,27838,23.976024
|
||||
27844,27939,23.976024
|
||||
28000,28579,23.976024
|
||||
28685,28812,23.976024
|
||||
28833,28836,23.976024
|
||||
28867,28870,23.976024
|
||||
28876,28963,23.976024
|
||||
28969,28988,23.976024
|
||||
28994,28997,23.976024
|
||||
29008,29015,23.976024
|
||||
29021,29036,23.976024
|
||||
29047,29066,23.976024
|
||||
29072,29219,23.976024
|
||||
29265,29268,23.976024
|
||||
29284,29287,23.976024
|
||||
29298,29313,23.976024
|
||||
29329,29496,23.976024
|
||||
29502,29505,23.976024
|
||||
29541,29544,23.976024
|
||||
29555,29558,23.976024
|
||||
29669,29672,23.976024
|
||||
29683,29686,23.976024
|
||||
29707,29710,23.976024
|
||||
29721,29808,23.976024
|
||||
29814,29845,23.976024
|
||||
29901,29976,23.976024
|
||||
30067,30074,23.976024
|
||||
30085,30092,23.976024
|
||||
30103,30222,23.976024
|
||||
30318,30697,23.976024
|
||||
30703,30742,23.976024
|
||||
30748,30811,23.976024
|
||||
30817,30872,23.976024
|
||||
30928,31019,23.976024
|
||||
31025,31200,23.976024
|
||||
31206,31217,23.976024
|
||||
31223,31330,23.976024
|
||||
31331,31333,17.982018
|
||||
31334,31353,23.976024
|
||||
31359,31362,23.976024
|
||||
31368,31679,23.976024
|
||||
31720,31763,23.976024
|
||||
31769,32036,23.976024
|
||||
32077,32088,23.976024
|
||||
32094,32149,23.976024
|
||||
32155,32182,23.976024
|
||||
32208,32215,23.976024
|
||||
32221,32240,23.976024
|
||||
32246,32277,23.976024
|
||||
32283,32306,23.976024
|
||||
32312,32843,23.976024
|
||||
32869,32896,23.976024
|
||||
32912,32927,23.976024
|
||||
32938,32961,23.976024
|
||||
33107,33122,23.976024
|
||||
33148,33223,23.976024
|
||||
33229,33236,23.976024
|
||||
33257,33260,23.976024
|
||||
33271,33274,23.976024
|
||||
33285,33312,23.976024
|
||||
33323,33330,23.976024
|
||||
33336,33339,23.976024
|
||||
33345,33352,23.976024
|
||||
33383,33402,23.976024
|
||||
33468,33471,23.976024
|
||||
33557,33560,23.976024
|
||||
33566,33585,23.976024
|
||||
33651,33714,23.976024
|
||||
33725,33740,23.976024
|
||||
33751,33782,23.976024
|
||||
33788,34047,23.976024
|
||||
34053,36444,23.976024
|
||||
# vfr stats: 71.60% film 28.40% video
|
||||
# vfr stats: 30461 - film 12080 - video 42541 - total
|
||||
# vfr stats: longest vid section - 1455 frames
|
||||
# vfr stats: # of detected vid sections - 245
|
|
@ -0,0 +1,3 @@
|
|||
# timecode format v1
|
||||
Assume 29.970030
|
||||
-10,10,25
|
|
@ -0,0 +1,4 @@
|
|||
# timecode format v1
|
||||
Assume 29.970030
|
||||
50,100,25
|
||||
0,10,25
|
|
@ -0,0 +1,3 @@
|
|||
# timecode format v1
|
||||
Assume 29.970030
|
||||
50,100,1000.1
|
|
@ -0,0 +1,3 @@
|
|||
# timecode format v1
|
||||
Assume 29.970030
|
||||
0,10,0
|
|
@ -0,0 +1,4 @@
|
|||
# timecode format v1
|
||||
Assume 29.970030
|
||||
0,1,25
|
||||
1,2,25
|
|
@ -0,0 +1,7 @@
|
|||
# timecode format v1
|
||||
Assume 29.970030
|
||||
0,0,25
|
||||
1,1,25
|
||||
2,2,25
|
||||
3,3,25
|
||||
4,4,25
|
|
@ -0,0 +1,3 @@
|
|||
# timecode format v1
|
||||
Assume 29.970030
|
||||
0,10
|
|
@ -0,0 +1,3 @@
|
|||
# timecode format v1
|
||||
Assume 29.970030
|
||||
0,10,24,8
|
|
@ -0,0 +1,3 @@
|
|||
# timecode format v1
|
||||
Assume 29.970030
|
||||
0, 10 , 25
|
|
@ -0,0 +1,101 @@
|
|||
# timecode format v2
|
||||
0.000000
|
||||
33.000000
|
||||
67.000000
|
||||
100.000000
|
||||
133.000000
|
||||
167.000000
|
||||
200.000000
|
||||
233.000000
|
||||
267.000000
|
||||
300.000000
|
||||
333.000000
|
||||
367.000000
|
||||
400.000000
|
||||
433.000000
|
||||
467.000000
|
||||
500.000000
|
||||
533.000000
|
||||
567.000000
|
||||
600.000000
|
||||
633.000000
|
||||
667.000000
|
||||
700.000000
|
||||
733.000000
|
||||
767.000000
|
||||
800.000000
|
||||
833.000000
|
||||
867.000000
|
||||
900.000000
|
||||
933.000000
|
||||
967.000000
|
||||
1000.000000
|
||||
1033.000000
|
||||
1067.000000
|
||||
1100.000000
|
||||
1133.000000
|
||||
1167.000000
|
||||
1200.000000
|
||||
1233.000000
|
||||
1267.000000
|
||||
1300.000000
|
||||
1333.000000
|
||||
1367.000000
|
||||
1400.000000
|
||||
1433.000000
|
||||
1467.000000
|
||||
1500.000000
|
||||
1533.000000
|
||||
1567.000000
|
||||
1600.000000
|
||||
1633.000000
|
||||
1667.000000
|
||||
1700.000000
|
||||
1733.000000
|
||||
1767.000000
|
||||
1800.000000
|
||||
1833.000000
|
||||
1867.000000
|
||||
1900.000000
|
||||
1933.000000
|
||||
1967.000000
|
||||
2000.000000
|
||||
2033.000000
|
||||
2067.000000
|
||||
2100.000000
|
||||
2133.000000
|
||||
2167.000000
|
||||
2200.000000
|
||||
2233.000000
|
||||
2267.000000
|
||||
2300.000000
|
||||
2333.000000
|
||||
2367.000000
|
||||
2400.000000
|
||||
2433.000000
|
||||
2467.000000
|
||||
2500.000000
|
||||
2533.000000
|
||||
2567.000000
|
||||
2600.000000
|
||||
2633.000000
|
||||
2667.000000
|
||||
2700.000000
|
||||
2733.000000
|
||||
2767.000000
|
||||
2800.000000
|
||||
2833.000000
|
||||
2867.000000
|
||||
2900.000000
|
||||
2933.000000
|
||||
2967.000000
|
||||
3000.000000
|
||||
3033.000000
|
||||
3067.000000
|
||||
3100.000000
|
||||
3133.000000
|
||||
3167.000000
|
||||
3200.000000
|
||||
3233.000000
|
||||
3267.000000
|
||||
3300.000000
|
|
@ -0,0 +1,101 @@
|
|||
# timecode format v2
|
||||
0
|
||||
33
|
||||
67
|
||||
100
|
||||
133
|
||||
167
|
||||
200
|
||||
233
|
||||
267
|
||||
300
|
||||
333
|
||||
367
|
||||
400
|
||||
433
|
||||
467
|
||||
500
|
||||
533
|
||||
567
|
||||
600
|
||||
633
|
||||
667
|
||||
719
|
||||
772
|
||||
825
|
||||
877
|
||||
930
|
||||
982
|
||||
1035
|
||||
1088
|
||||
1140
|
||||
1193
|
||||
1246
|
||||
1298
|
||||
1351
|
||||
1404
|
||||
1456
|
||||
1509
|
||||
1561
|
||||
1614
|
||||
1667
|
||||
1719
|
||||
1772
|
||||
1825
|
||||
1877
|
||||
1930
|
||||
1982
|
||||
2035
|
||||
2088
|
||||
2140
|
||||
2193
|
||||
2246
|
||||
2298
|
||||
2332
|
||||
2365
|
||||
2398
|
||||
2432
|
||||
2465
|
||||
2498
|
||||
2532
|
||||
2565
|
||||
2598
|
||||
2632
|
||||
2665
|
||||
2698
|
||||
2732
|
||||
2765
|
||||
2798
|
||||
2832
|
||||
2865
|
||||
2898
|
||||
2932
|
||||
2965
|
||||
2998
|
||||
3032
|
||||
3065
|
||||
3098
|
||||
3132
|
||||
3165
|
||||
3198
|
||||
3232
|
||||
3265
|
||||
3298
|
||||
3332
|
||||
3365
|
||||
3398
|
||||
3432
|
||||
3465
|
||||
3498
|
||||
3532
|
||||
3565
|
||||
3598
|
||||
3632
|
||||
3665
|
||||
3698
|
||||
3732
|
||||
3765
|
||||
3798
|
||||
3832
|
||||
3865
|
||||
3898
|
|
@ -0,0 +1,10 @@
|
|||
# timecode format v2
|
||||
0000.00000
|
||||
1000.00000
|
||||
2000.00000
|
||||
3000.00000
|
||||
4000.00000
|
||||
5000.00000
|
||||
6000.00000
|
||||
7000.00000
|
||||
8000.00000
|
|
@ -0,0 +1,15 @@
|
|||
# timecode format v2
|
||||
0000.00000
|
||||
# why does this format even have comments?
|
||||
1000.00000
|
||||
# I can see them being vaguely useful in v1 timecodes
|
||||
2000.00000
|
||||
# I guess we could stick the name of the writing program in a comment
|
||||
3000.00000
|
||||
# but really, what's the point?
|
||||
4000.00000
|
||||
#nospace
|
||||
5000.00000
|
||||
6000.00000
|
||||
7000.00000
|
||||
8000.00000
|
|
@ -0,0 +1 @@
|
|||
# timecode format v2
|
|
@ -0,0 +1,11 @@
|
|||
# timecode format v2
|
||||
0.00000
|
||||
100.00000
|
||||
200.00000
|
||||
201.00000
|
||||
202.00000
|
||||
203.00000
|
||||
204.00000
|
||||
205.00000
|
||||
206.00000
|
||||
207.00000
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,4 @@
|
|||
# timecode format v2
|
||||
0.00000
|
||||
100.00000
|
||||
200.00000
|
|
@ -0,0 +1,11 @@
|
|||
# timecode format v2
|
||||
0000.00000
|
||||
#1000.00000
|
||||
1000.00000
|
||||
2000.00000
|
||||
3000.00000
|
||||
4000.00000
|
||||
5000.00000
|
||||
6000.00000
|
||||
7000.00000
|
||||
8000.00000
|
|
@ -0,0 +1,4 @@
|
|||
# timecode format v2
|
||||
0000.00000
|
||||
2000.00000
|
||||
1000.00000
|
|
@ -0,0 +1,5 @@
|
|||
# timecode format v2
|
||||
0
|
||||
100
|
||||
200
|
||||
300
|
|
@ -0,0 +1,5 @@
|
|||
# timecode format v2
|
||||
0
|
||||
100
|
||||
201
|
||||
300
|
|
@ -0,0 +1,6 @@
|
|||
# timecode format v2
|
||||
0
|
||||
100
|
||||
200
|
||||
300
|
||||
400
|
|
@ -0,0 +1,4 @@
|
|||
# timecode format v2
|
||||
0
|
||||
100
|
||||
200
|
Loading…
Reference in New Issue