Fix significantly incorrect handling of pretty much everything in AssTransformFramerateFilter and make it better at deciding when it actually needs to do anything.

Originally committed to SVN as r4578.
This commit is contained in:
Thomas Goyne 2010-06-24 01:24:26 +00:00
parent 627d423017
commit 53fb43c7e6
5 changed files with 138 additions and 414 deletions

View file

@ -34,29 +34,48 @@
/// @ingroup export
///
///////////
// Headers
#include "config.h"
#ifndef AGI_PRE
#include <utility>
#include <wx/button.h>
#include <wx/checkbox.h>
#include <wx/panel.h>
#include <wx/radiobut.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#endif
#include "ass_dialogue.h"
#include "ass_file.h"
#include "ass_override.h"
#include "export_framerate.h"
#include "vfr.h"
#include "utils.h"
/// @brief Constructor
/// DOCME
/// @class LineData
/// @brief DOCME
///
/// DOCME
struct LineData {
AssDialogue *line;
int newStart;
int newEnd;
int newK;
int oldK;
};
/// IDs
enum {
Get_Input_From_Video = 2000
};
AssTransformFramerateFilter::AssTransformFramerateFilter() {
initialized = false;
}
/// @brief Init
/// @return
///
void AssTransformFramerateFilter::Init() {
if (initialized) return;
initialized = true;
@ -67,27 +86,10 @@ void AssTransformFramerateFilter::Init() {
Output = NULL;
}
/// @brief Process
/// @param subs
/// @param export_dialog
///
void AssTransformFramerateFilter::ProcessSubs(AssFile *subs, wxWindow *export_dialog) {
// Transform frame rate
if (Input->IsLoaded() && Output->IsLoaded()) {
if (Input->GetFrameRateType() == VFR || Output->GetFrameRateType() == VFR || Output->GetAverage() != Input->GetAverage()) {
TransformFrameRate(subs);
}
}
TransformFrameRate(subs);
}
/// @brief Get dialog
/// @param parent
/// @return
///
wxWindow *AssTransformFramerateFilter::GetConfigDialogWindow(wxWindow *parent) {
wxWindow *base = new wxPanel(parent, -1);
@ -146,11 +148,6 @@ wxWindow *AssTransformFramerateFilter::GetConfigDialogWindow(wxWindow *parent) {
return base;
}
/// @brief Load settings
/// @param IsDefault
///
void AssTransformFramerateFilter::LoadSettings(bool IsDefault) {
if (IsDefault) {
Input = &VFR_Input;
@ -168,149 +165,91 @@ void AssTransformFramerateFilter::LoadSettings(bool IsDefault) {
}
else Output = &VFR_Output;
// Reverse
if (Reverse->IsChecked()) {
FrameRate *temp = Output;
Output = Input;
Input = temp;
std::swap(Input, Output);
}
}
}
/// Truncate a time to centisecond precision
int FORCEINLINE trunc_cs(int time) {
return (time / 10) * 10;
}
/// @brief Transform framerate in tags
/// @param name
/// @param n
/// @param curParam
/// @param curData
/// @return
///
void AssTransformFramerateFilter::TransformTimeTags (wxString name,int n,AssOverrideParameter *curParam,void *curData) {
// Only modify anything if this is a number
VariableDataType type = curParam->GetType();
if (type != VARDATA_INT && type != VARDATA_FLOAT) return;
// Setup
LineData *lineData = (LineData*) curData;
AssDialogue *curDiag = lineData->line;;
bool start = true;
bool karaoke = false;
int mult = 1;
int value;
LineData *lineData = static_cast<LineData*>(curData);
AssDialogue *curDiag = lineData->line;
int parVal = curParam->Get<int>();
switch (curParam->classification) {
case PARCLASS_RELATIVE_TIME_START:
case PARCLASS_RELATIVE_TIME_START: {
int value = instance.ConvertTime(trunc_cs(curDiag->Start.GetMS()) + parVal) - lineData->newStart;
// An end time of 0 is actually the end time of the line, so ensure
// nonzero is never converted to 0
// Needed here rather than the end case because start/end here mean
// which end of the line the time is relative to, not whether it's
// the start or end time (compare \move and \fad)
if (value == 0 && parVal != 0) value = 1;
curParam->Set(value);
break;
}
case PARCLASS_RELATIVE_TIME_END:
start = false;
curParam->Set(lineData->newEnd - instance.ConvertTime(trunc_cs(curDiag->End.GetMS()) - parVal));
break;
case PARCLASS_KARAOKE:
karaoke = true;
mult = 10;
case PARCLASS_KARAOKE: {
int start = curDiag->Start.GetMS() / 10 + lineData->oldK + parVal;
int value = (instance.ConvertTime(start * 10) - lineData->newStart) / 10 - lineData->newK;
lineData->oldK += parVal;
lineData->newK += value;
curParam->Set(value);
break;
}
default:
return;
}
// Parameter value
int parVal = curParam->Get<int>() * mult;
// Karaoke preprocess
int curKarPos = 0;
if (karaoke) {
if (name == _T("\\k")) {
curKarPos = lineData->k;
lineData->k += parVal/10;
}
else if (name == _T("\\K") || name == _T("\\kf")) {
curKarPos = lineData->kf;
lineData->kf += parVal/10;
}
else if (name == _T("\\ko")) {
curKarPos = lineData->ko;
lineData->ko += parVal/10;
}
else throw wxString::Format(_T("Unknown karaoke tag! '%s'"), name.c_str());
curKarPos *= 10;
parVal += curKarPos;
}
// Start time
if (start) {
int newStart = instance.Input->GetTimeAtFrame(instance.Output->GetFrameAtTime(curDiag->Start.GetMS()));
int absTime = curDiag->Start.GetMS() + parVal;
value = instance.Input->GetTimeAtFrame(instance.Output->GetFrameAtTime(absTime)) - newStart;
// An end time of 0 is actually the end time of the line, so ensure nonzero is never converted to 0
// Needed in the start case as well as the end one due to \t, whose end time needs the start time
// behavior
if (value == 0 && parVal != 0) value = 1;
}
// End time
else {
int newEnd = instance.Input->GetTimeAtFrame(instance.Output->GetFrameAtTime(curDiag->End.GetMS()));
int absTime = curDiag->End.GetMS() - parVal;
value = newEnd - instance.Input->GetTimeAtFrame(instance.Output->GetFrameAtTime(absTime));
if (value == 0 && parVal != 0) value = 1;
}
// Karaoke postprocess
if (karaoke) {
int post = instance.Input->GetTimeAtFrame(instance.Output->GetFrameAtTime(curDiag->Start.GetMS() + curKarPos));
int start = instance.Input->GetTimeAtFrame(instance.Output->GetFrameAtTime(curDiag->Start.GetMS()));
curKarPos = post-start;
value -= curKarPos;
}
curParam->Set<int>(value/mult);
}
/// @brief Transform framerate
/// @param subs
///
void AssTransformFramerateFilter::TransformFrameRate(AssFile *subs) {
int n=0;
// Run through
using std::list;
AssEntry *curEntry;
AssDialogue *curDialogue;
if (!Input->IsLoaded() || !Output->IsLoaded() || Input == Output || *Input == *Output) return;
for (entryIter cur=subs->Line.begin();cur!=subs->Line.end();cur++) {
curEntry = *cur;
// why the christ was this ever done to begin with?
// yes, let's framerate compensate the start timestamp and then use the changed value to
// compensate it AGAIN 20 lines down? I DO NOT GET IT
// -Fluff
//curEntry->Start.SetMS(Input->GetTimeAtFrame(Output->GetFrameAtTime(curEntry->GetStartMS(),true),true));
curDialogue = dynamic_cast<AssDialogue*>(curEntry);
AssDialogue *curDialogue = dynamic_cast<AssDialogue*>(*cur);
// Update dialogue entries
if (curDialogue) {
// Line data
LineData data;
data.line = curDialogue;
data.k = 0;
data.kf = 0;
data.ko = 0;
data.newK = 0;
data.oldK = 0;
data.newStart = trunc_cs(ConvertTime(curDialogue->Start.GetMS()));
data.newEnd = trunc_cs(ConvertTime(curDialogue->End.GetMS()));
// Process stuff
curDialogue->ParseASSTags();
curDialogue->ProcessParameters(TransformTimeTags,&data);
curDialogue->Start.SetMS(Input->GetTimeAtFrame(Output->GetFrameAtTime(curDialogue->Start.GetMS(),true),true));
curDialogue->End.SetMS(Input->GetTimeAtFrame(Output->GetFrameAtTime(curDialogue->End.GetMS(),false),false));
curDialogue->Start.SetMS(data.newStart);
curDialogue->End.SetMS(data.newEnd);
curDialogue->UpdateText();
curDialogue->ClearBlocks();
n++;
}
}
}
int AssTransformFramerateFilter::ConvertTime(int time) {
int frame = Output->GetFrameAtTime(time, false);
int frameStart = Output->GetTimeAtFrame(frame, false, true);
int frameEnd = Output->GetTimeAtFrame(frame + 1, false, true);
int frameDur = frameEnd - frameStart;
double dist = double(time - frameStart) / frameDur;
int newStart = Input->GetTimeAtFrame(frame, false, true);
int newEnd = Input->GetTimeAtFrame(frame + 1, false, true);
int newDur = newEnd - newStart;
return newStart + newDur * dist;
}
/// DOCME
AssTransformFramerateFilter AssTransformFramerateFilter::instance;

View file

@ -34,110 +34,61 @@
/// @ingroup export
///
///////////
// Headers
#ifndef AGI_PRE
#include <wx/button.h>
#include <wx/checkbox.h>
#include <wx/panel.h>
#include <wx/radiobut.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#endif
#include "ass_export_filter.h"
#include "vfr.h"
//////////////
// Prototypes
class AssOverrideParameter;
class AssDialogue;
class AssOverrideParameter;
class wxCheckBox;
class wxRadioButton;
class wxTextCtrl;
/// DOCME
/// @class AssTransformFramerateFilter
/// @brief DOCME
///
/// DOCME
class AssTransformFramerateFilter : public AssExportFilter {
private:
/// DOCME
/// The singleton instance of this filter
static AssTransformFramerateFilter instance;
/// DOCME
// Yes, these are backwards
FrameRate *Input; /// Destination frame rate
FrameRate *Output; /// Source frame rate
/// DOCME
FrameRate *Input,*Output;
/// DOCME
/// DOCME
FrameRate t1,t2;
wxTextCtrl *InputFramerate; /// Input frame rate text box
wxTextCtrl *OutputFramerate; /// Output frame rate text box
/// DOCME
wxTextCtrl *InputFramerate;
wxRadioButton *RadioOutputCFR; /// CFR radio control
wxRadioButton *RadioOutputVFR; /// VFR radio control
/// DOCME
wxTextCtrl *OutputFramerate;
/// DOCME
wxRadioButton *RadioOutputCFR;
/// DOCME
wxRadioButton *RadioOutputVFR;
/// DOCME
wxCheckBox *Reverse;
wxCheckBox *Reverse; /// Switch input and output
/// Constructor
AssTransformFramerateFilter();
/// @brief Apply the transformation to a file
/// @param subs File to process
void TransformFrameRate(AssFile *subs);
static void TransformTimeTags(wxString name,int n,AssOverrideParameter *curParam,void *_curDiag);
/// @brief Transform a single tag
/// @param name Name of the tag
/// @param curParam Current parameter being processed
/// @param userdata LineData passed
static void TransformTimeTags(wxString name,int,AssOverrideParameter *curParam,void *userdata);
/// Initialize the singleton instance
void Init();
/// @brief Convert a time from the input frame rate to the output frame rate
/// @param time Time in ms to convert
/// @return Time in ms
///
/// This preserves two things:
/// 1. The frame number
/// 2. The relative distance between the beginning of the frame which time
/// is in and the beginning of the next frame
int ConvertTime(int time);
public:
void ProcessSubs(AssFile *subs, wxWindow *export_dialog);
wxWindow *GetConfigDialogWindow(wxWindow *parent);
void LoadSettings(bool IsDefault);
};
/// DOCME
/// @class LineData
/// @brief DOCME
///
/// DOCME
class LineData {
public:
/// DOCME
AssDialogue *line;
/// DOCME
int k;
/// DOCME
int kf;
/// DOCME
int ko;
};
///////
// IDs
enum {
/// DOCME
Get_Input_From_Video = 2000
};

View file

@ -34,9 +34,6 @@
/// @ingroup video_input
///
///////////
// Headers
#include "config.h"
#ifndef AGI_PRE
@ -53,53 +50,36 @@
/// @brief V2 Clear function
///
void FrameRate::Clear () {
Frame.clear();
}
/// @brief V2 Add frame
/// @param ms
///
void FrameRate::AddFrame(int ms) {
Frame.push_back(ms);
}
/// @brief V2 Get Average
///
void FrameRate::CalcAverage() {
if (Frame.size() <= 1)
throw _("No timecodes to average");
AverageFrameRate = double(Frame.back()) / (Frame.size()-1);
}
/// @brief Constructor FrameRate //////////////////////
///
/// @brief Constructor
FrameRate::FrameRate() {
Unload();
}
/// @brief Destructor
///
FrameRate::~FrameRate() {
Unload();
}
/// @brief Loads VFR file
/// @param filename
///
void FrameRate::Load(wxString filename) {
using namespace std;
@ -241,11 +221,8 @@ void FrameRate::Load(wxString filename) {
config::mru->Add("Timecodes", STD_STR(filename));
}
/// @brief Save
/// @param filename
///
void FrameRate::Save(wxString filename) {
TextFileWriter file(filename,_T("ASCII"));
file.WriteLineToFile(_T("# timecode format v2"));
@ -254,10 +231,7 @@ void FrameRate::Save(wxString filename) {
}
}
/// @brief Unload
///
void FrameRate::Unload () {
FrameRateType = NONE;
AverageFrameRate = 0;
@ -268,11 +242,8 @@ void FrameRate::Unload () {
vfrFile = _T("");
}
/// @brief Sets to CFR
/// @param fps
///
void FrameRate::SetCFR(double fps) {
Unload();
loaded = true;
@ -280,13 +251,9 @@ void FrameRate::SetCFR(double fps) {
AverageFrameRate = fps;
}
/// @brief Sets to VFR
/// @param newTimes
///
void FrameRate::SetVFR(std::vector<int> newTimes) {
// Prepare
Unload();
loaded = true;
@ -299,22 +266,16 @@ void FrameRate::SetVFR(std::vector<int> newTimes) {
last_frame = (int)newTimes.size();
}
/// @brief Gets frame number at time
/// @param ms
/// @param useceil
/// @return
///
int FrameRate::PFrameAtTime(int ms,bool useceil) {
int FrameRate::PFrameAtTime(int ms,bool useceil) const {
// Check if it's loaded
if (!loaded) return -1;
// Normalize miliseconds
ms = MAX(ms,0);
// Get for constant frame rate
if (FrameRateType == CFR || Frame.size() == 0) {
if (FrameRateType == CFR || Frame.size() == 0 || ms < 0) {
double value = double(ms) * AverageFrameRate / 1000.0;
if (useceil) return (int)ceil(value);
else return (int)floor(value);
@ -366,13 +327,10 @@ int FrameRate::PFrameAtTime(int ms,bool useceil) {
return -1;
}
/// @brief Gets time at frame
/// @param frame
/// @return
///
int FrameRate::PTimeAtFrame(int frame) {
int FrameRate::PTimeAtFrame(int frame) const {
// Not loaded
if (!loaded) return -1;
@ -397,26 +355,20 @@ int FrameRate::PTimeAtFrame(int frame) {
return -1;
}
/// @brief otherwise for start frames returns the adjusted time for end frames when start=false Get correct frame at time
/// @param ms
/// @param start
/// @return
///
int FrameRate::GetFrameAtTime(int ms,bool start) {
int FrameRate::GetFrameAtTime(int ms,bool start) const {
return PFrameAtTime(ms,start);
}
/// @brief compensates and returns an end time when start=false Get correct time at frame
/// @param frame
/// @param start
/// @param exact
/// @param frame Frame number
/// @param start Adjust for start time
/// @param exact Don't do awful things to avoid rounding errors
/// @return
///
int FrameRate::GetTimeAtFrame(int frame,bool start,bool exact) {
int FrameRate::GetTimeAtFrame(int frame,bool start,bool exact) const {
int finalTime;
// Exact, for display
@ -439,121 +391,21 @@ int FrameRate::GetTimeAtFrame(int frame,bool start,bool exact) {
return finalTime;
}
/// @brief Get the current list of frames/times
/// @return
///
std::vector<int> FrameRate::GetFrameTimeList() {
std::vector<int> FrameRate::GetFrameTimeList() const {
return Frame;
}
/// @brief e.g., in a mix of 24fps and 30fps, returns 120fps Calculate the common FPS for evil stuff
/// @return
///
double FrameRate::GetCommonFPS() {
// Variables
int curDist;
int lastDist = 0;
int sectionStart = 0;
double curFps;
// List of likely frame rates
std::vector<double> frameRates;
frameRates.push_back(15.0 / 1.001);
frameRates.push_back(15);
frameRates.push_back(24.0 / 1.001);
frameRates.push_back(24);
frameRates.push_back(30.0 / 1.001);
frameRates.push_back(30);
frameRates.push_back(120.0 / 1.001);
frameRates.push_back(120);
// List of rates found
std::vector<double> found;
// Find the relative fps of each area
for (unsigned int i=1;i<Frame.size();i++) {
// Find the current frame distance
curDist = Frame[i]-Frame[i-1];
// See if it's close enough to the last
if ((abs(curDist - lastDist) < 2 || i-1 == (unsigned) sectionStart) && i != Frame.size()-1) {
lastDist = curDist;
continue;
}
// Calculate section fps
curFps = (i - sectionStart - 1) * 1000.0 / double(Frame[i-1]-Frame[sectionStart]);
sectionStart = i;
lastDist = curDist;
// See if it's close enough to one of the likely rates
for (unsigned int j=0;j<frameRates.size();j++) {
if (curFps-0.01 <= frameRates[j] && curFps+0.01 >= frameRates[j]) {
curFps = frameRates[j];
break;
}
}
// See if it's on list
bool onList = false;
for (unsigned int j=0;j<found.size();j++) {
if (found[j] == curFps) {
onList = true;
break;
}
}
// If not, add it
if (!onList) found.push_back(curFps);
}
// Find common between them
double v1,v2,minInt,tempd;
int tempi1,tempi2;
while (found.size() > 1) {
// Extract last two values
v1 = found.back();
found.pop_back();
v2 = found.back();
found.pop_back();
// Divide them
v2 = v1/v2;
// Find what it takes to make it an integer
for (minInt = 1;minInt<20;minInt++) {
tempd = v2 * minInt;
tempi1 = (int)(tempd-0.001);
tempi2 = (int)(tempd+0.001);
if (tempi1 != tempi2) break;
}
if (minInt != 20) v1 = v1*minInt;
// See if it's close enough to one of the likely rates
for (unsigned int j=0;j<frameRates.size();j++) {
if (v1-0.01 <= frameRates[j] && v1+0.01 >= frameRates[j]) {
v1 = frameRates[j];
break;
}
}
// Re-insert obtained result
found.push_back(v1);
}
return found.back();
bool FrameRate::operator==(FrameRate const& rgt) {
if (FrameRateType != rgt.FrameRateType) return false;
if (FrameRateType == NONE) return true;
if (FrameRateType == CFR) return AverageFrameRate == rgt.AverageFrameRate;
return Frame == rgt.Frame;
}
/// DOCME
FrameRate VFR_Output;
/// DOCME
FrameRate VFR_Input;

View file

@ -42,9 +42,6 @@
#pragma once
///////////
// Headers
#ifndef AGI_PRE
#include <list>
#include <vector>
@ -53,19 +50,10 @@
#include <wx/string.h>
#endif
#include "include/aegisub/aegisub.h"
/// DOCME
enum ASS_FrameRateType {
/// DOCME
NONE,
/// DOCME
CFR,
/// DOCME
VFR
};
@ -97,8 +85,8 @@ private:
void Clear();
void CalcAverage();
int PFrameAtTime(int ms,bool useCeil=false);
int PTimeAtFrame(int frame);
int PFrameAtTime(int ms,bool useCeil=false) const;
int PTimeAtFrame(int frame) const;
/// DOCME
@ -122,37 +110,33 @@ public:
void Save(wxString file);
void Unload();
int GetFrameAtTime(int ms,bool start=true);
int GetTimeAtFrame(int frame,bool start=true,bool exact=false);
int GetFrameAtTime(int ms,bool start=true) const;
int GetTimeAtFrame(int frame,bool start=true,bool exact=false) const;
/// @brief DOCME
/// @return
///
double GetAverage() { return AverageFrameRate; };
double GetAverage() const { return AverageFrameRate; };
/// @brief DOCME
/// @return
///
bool IsLoaded() { return loaded; };
bool IsLoaded() const { return loaded; };
/// @brief DOCME
/// @return
///
ASS_FrameRateType GetFrameRateType() { return FrameRateType; };
ASS_FrameRateType GetFrameRateType() const { return FrameRateType; };
/// @brief DOCME
///
wxString GetFilename() { return vfrFile; };
wxString GetFilename() const { return vfrFile; };
std::vector<int> GetFrameTimeList();
double GetCommonFPS();
std::vector<int> GetFrameTimeList() const;
bool operator==(FrameRate const& rgt);
};
///////////
// Globals
extern FrameRate VFR_Output;
extern FrameRate VFR_Input;

View file

@ -255,7 +255,6 @@ PClip AvisynthVideoProvider::OpenVideo(wxString _filename, bool mpeg2dec3_priori
// Read keyframes and timecodes from MKV file
isVfr = false;
FrameRate temp;
double overFps = 0;
bool mkvOpen = MatroskaWrapper::wrapper.IsOpen();
KeyFrames.Clear();
if (extension == _T(".mkv") || mkvOpen) {
@ -273,7 +272,6 @@ PClip AvisynthVideoProvider::OpenVideo(wxString _filename, bool mpeg2dec3_priori
MatroskaWrapper::wrapper.SetToTimecodes(temp);
isVfr = temp.GetFrameRateType() == VFR;
if (isVfr) {
overFps = temp.GetCommonFPS();
MatroskaWrapper::wrapper.SetToTimecodes(VFR_Input);
MatroskaWrapper::wrapper.SetToTimecodes(VFR_Output);
trueFrameRate = temp;