Added VFR support. (Untested, as usual.)

Originally committed to SVN as r1477.
This commit is contained in:
Niels Martin Hansen 2007-08-11 22:06:01 +00:00
parent 0a584a7cd4
commit 06452148d2
4 changed files with 231 additions and 1 deletions

View file

@ -31,6 +31,7 @@
#include "avisynth.h"
#include "overlua.h"
#include "vfr.h"
// Lots of code lifted from the CSRI avisynth.cpp
@ -38,6 +39,7 @@ class OverLuaAvisynth : public GenericVideoFilter {
private:
OverLuaScript *script;
double spf; // seconds per frame - for frame/timestamp conversion
VFRTranslator *vfr;
public:
OverLuaAvisynth(PClip _child, IScriptEnvironment *env, const char *file, const char *datastring, const char *vfrfile)
@ -55,6 +57,10 @@ public:
try {
script = new OverLuaScript(file, datastring);
spf = (double)vi.fps_denominator / (double)vi.fps_numerator;
if (vfrfile)
vfr = GetVFRTranslator(vfrfile);
else
vfr = 0;
}
catch (const char *e) {
env->ThrowError(e);
@ -66,6 +72,8 @@ public:
~OverLuaAvisynth()
{
if (vfr)
delete vfr;
delete script;
}
@ -76,6 +84,8 @@ public:
env->MakeWritable(&avsframe);
double frametime = n * spf;
if (vfr)
frametime = vfr->TimeStampFromFrameNumber(n);
ptrdiff_t stride = avsframe->GetPitch();
unsigned char *plane = avsframe->GetWritePtr();

View file

@ -20,7 +20,6 @@ subtitles/karaoke.
vfrfile is the path to a timecode file Matroska format 1 or 2. If supplied,
it will be used to translate frame numbers to timestamps instead of relying
on the frame rate provided by Avisynth.
VFR support is not implemented yet.
API the Lua script must implement

184
OverLua/vfr.cpp Normal file
View file

@ -0,0 +1,184 @@
/*
* 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 &sect = 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;
}

37
OverLua/vfr.h Normal file
View file

@ -0,0 +1,37 @@
/*
* 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
*/
#ifndef VFR_H
#define VFR_H
class VFRTranslator {
public:
virtual double TimeStampFromFrameNumber(int n) = 0;
};
VFRTranslator *GetVFRTranslator(const char *vfrfile);
#endif