forked from mia/Aegisub
185 lines
4.9 KiB
C++
185 lines
4.9 KiB
C++
|
/*
|
||
|
* VFR translation functions for OverLua
|
||
|
*
|
||
|
|
||
|
Copyright 2007 Niels Martin Hansen
|
||
|
|
||
|
This program is free software; you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation; either version 2 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License along
|
||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||
|
|
||
|
Contact:
|
||
|
E-mail: <jiifurusu@gmail.com>
|
||
|
IRC: jfs in #aegisub on irc.rizon.net
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include "vfr.h"
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <vector>
|
||
|
|
||
|
|
||
|
// Work with seconds per frame (spf) here instead of fps since that's more natural for the translation we're doing
|
||
|
|
||
|
|
||
|
class TimecodesV1 : public VFRTranslator {
|
||
|
private:
|
||
|
// Used when sections run out
|
||
|
double default_spf;
|
||
|
double first_non_section_timestamp;
|
||
|
int first_non_section_frame;
|
||
|
|
||
|
// Also generate sections for unspecified ones
|
||
|
// (use the default framerate then)
|
||
|
struct FrameRateSection {
|
||
|
double start_time;
|
||
|
double spf;
|
||
|
int start_frame;
|
||
|
int end_frame;
|
||
|
};
|
||
|
std::vector<FrameRateSection> sections;
|
||
|
|
||
|
public:
|
||
|
virtual double TimeStampFromFrameNumber(int n)
|
||
|
{
|
||
|
// Find correct section
|
||
|
for (size_t i = 0; i < sections.size(); i++) {
|
||
|
FrameRateSection § = sections[i];
|
||
|
if (n >= sect.start_frame && n <= sect.end_frame) {
|
||
|
return sect.start_time + (n - sect.start_frame) * sect.spf;
|
||
|
}
|
||
|
}
|
||
|
// Not in a section
|
||
|
if (n < 0) return 0.0;
|
||
|
return first_non_section_timestamp + (n - first_non_section_frame) * default_spf;
|
||
|
}
|
||
|
|
||
|
TimecodesV1(FILE *vfrfile)
|
||
|
{
|
||
|
char buf[100];
|
||
|
|
||
|
default_spf = -1;
|
||
|
double cur_time = 0.0;
|
||
|
|
||
|
FrameRateSection temp_section;
|
||
|
temp_section.start_time = 0.0;
|
||
|
temp_section.spf = -1;
|
||
|
temp_section.start_frame = 0;
|
||
|
temp_section.end_frame = 0;
|
||
|
|
||
|
while (fgets(buf, 100, vfrfile)) {
|
||
|
// Comment?
|
||
|
if (buf[0] == '#') continue;
|
||
|
|
||
|
if (strncmp(buf, "Assume ", 7) == 0 && default_spf < 0) {
|
||
|
char *num = buf+7;
|
||
|
default_spf = atof(num);
|
||
|
if (default_spf > 0)
|
||
|
default_spf = 1 / default_spf;
|
||
|
else
|
||
|
default_spf = -1;
|
||
|
temp_section.spf = default_spf;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
int start_frame, end_frame;
|
||
|
double fps;
|
||
|
if (scanf("%d,%d,%f", &start_frame, &end_frame, &fps) == 3) {
|
||
|
// Finish the current temp section
|
||
|
temp_section.end_frame = start_frame - 1;
|
||
|
if (temp_section.end_frame >= temp_section.start_frame) {
|
||
|
cur_time += (temp_section.end_frame - temp_section.start_frame + 1) * temp_section.spf;
|
||
|
sections.push_back(temp_section);
|
||
|
}
|
||
|
// Insert the section corresponding to this line
|
||
|
temp_section.spf = 1/fps;
|
||
|
temp_section.start_frame = start_frame;
|
||
|
temp_section.end_frame = end_frame;
|
||
|
temp_section.start_time = cur_time;
|
||
|
cur_time += (end_frame - start_frame + 1) / fps;
|
||
|
sections.push_back(temp_section);
|
||
|
// Begin new temp section
|
||
|
temp_section.spf = default_spf;
|
||
|
temp_section.start_frame = end_frame + 1;
|
||
|
temp_section.end_frame = end_frame; // yes, negative duration
|
||
|
temp_section.start_time = cur_time;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
first_non_section_timestamp = cur_time;
|
||
|
first_non_section_frame = temp_section.start_frame;
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
class TimecodesV2 : public VFRTranslator {
|
||
|
private:
|
||
|
// Main data
|
||
|
std::vector<double> timestamps;
|
||
|
// For when data are exhausted (well, they shouldn't, then the vfr file is bad)
|
||
|
int last_known_frame;
|
||
|
double last_known_timestamp;
|
||
|
double assumed_spf;
|
||
|
|
||
|
public:
|
||
|
virtual double TimeStampFromFrameNumber(int n)
|
||
|
{
|
||
|
if (n < (int)timestamps.size() && n >= 0) {
|
||
|
return timestamps[n];
|
||
|
}
|
||
|
if (n < 0) return 0.0;
|
||
|
return last_known_timestamp + (n - last_known_frame) * assumed_spf;
|
||
|
}
|
||
|
|
||
|
TimecodesV2(FILE *vfrfile)
|
||
|
{
|
||
|
char buf[50];
|
||
|
|
||
|
timestamps.reserve(8192); // should be enough for most cases
|
||
|
|
||
|
while (fgets(buf, 50, vfrfile)) {
|
||
|
// Comment?
|
||
|
if (buf[0] == '#') continue;
|
||
|
// Otherwise assume it's a good timestamp
|
||
|
timestamps.push_back(atof(buf));
|
||
|
}
|
||
|
|
||
|
last_known_frame = (int)timestamps.size()-1;
|
||
|
last_known_timestamp = timestamps[last_known_frame];
|
||
|
assumed_spf = 1/25.0; // yes, this is stupid - anyone got a better idea?
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
VFRTranslator *GetVFRTranslator(const char *vfrfile)
|
||
|
{
|
||
|
char buf[32];
|
||
|
buf[19] = 0; // In "# timecode format v1" the version number is character index 19
|
||
|
FILE *f = fopen(vfrfile, "r");
|
||
|
VFRTranslator *res = 0;
|
||
|
if (fgets(buf, 32, f) && buf[0] == '#') {
|
||
|
// So do some really shoddy parsing here, assume the file is good
|
||
|
if (buf[19] == '1') {
|
||
|
res = new TimecodesV1(f);
|
||
|
} else if (buf[19] == '2') {
|
||
|
res = new TimecodesV2(f);
|
||
|
}
|
||
|
}
|
||
|
fclose(f);
|
||
|
return res;
|
||
|
}
|
||
|
|