~ubuntu-branches/ubuntu/trusty/aegisub/trusty

« back to all changes in this revision

Viewing changes to src/vfr.cpp

  • Committer: Package Import Robot
  • Author(s): Sebastian Reichel
  • Date: 2012-03-16 22:58:00 UTC
  • Revision ID: package-import@ubuntu.com-20120316225800-yfb8h9e5n04rk46a
Tags: upstream-2.1.9
ImportĀ upstreamĀ versionĀ 2.1.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright (c) 2005-2006, Rodrigo Braz Monteiro, Fredrik Mellbin
 
2
// All rights reserved.
 
3
//
 
4
// Redistribution and use in source and binary forms, with or without
 
5
// modification, are permitted provided that the following conditions are met:
 
6
//
 
7
//   * Redistributions of source code must retain the above copyright notice,
 
8
//     this list of conditions and the following disclaimer.
 
9
//   * Redistributions in binary form must reproduce the above copyright notice,
 
10
//     this list of conditions and the following disclaimer in the documentation
 
11
//     and/or other materials provided with the distribution.
 
12
//   * Neither the name of the Aegisub Group nor the names of its contributors
 
13
//     may be used to endorse or promote products derived from this software
 
14
//     without specific prior written permission.
 
15
//
 
16
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 
17
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
18
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
19
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 
20
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 
21
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 
22
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 
23
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 
24
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
25
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
26
// POSSIBILITY OF SUCH DAMAGE.
 
27
//
 
28
// -----------------------------------------------------------------------------
 
29
//
 
30
// AEGISUB
 
31
//
 
32
// Website: http://aegisub.cellosoft.com
 
33
// Contact: mailto:zeratul@cellosoft.com
 
34
//
 
35
 
 
36
 
 
37
///////////
 
38
// Headers
 
39
#include "config.h"
 
40
 
 
41
#include <wx/filename.h>
 
42
#include "options.h"
 
43
#include "vfr.h"
 
44
#include "utils.h"
 
45
#include "text_file_reader.h"
 
46
#include "text_file_writer.h"
 
47
 
 
48
 
 
49
/////////////////////
 
50
// V2 Clear function
 
51
void FrameRate::Clear () {
 
52
        Frame.clear();
 
53
}
 
54
 
 
55
 
 
56
////////////////
 
57
// V2 Add frame
 
58
void FrameRate::AddFrame(int ms) {
 
59
        Frame.push_back(ms);
 
60
}
 
61
 
 
62
 
 
63
//////////////////
 
64
// V2 Get Average
 
65
void FrameRate::CalcAverage() {
 
66
 
 
67
        if (Frame.size() <= 1)
 
68
                throw _("No timecodes to average");
 
69
 
 
70
        AverageFrameRate = double(Frame.back()) / (Frame.size()-1);
 
71
}
 
72
 
 
73
 
 
74
//////////////////////// FrameRate //////////////////////
 
75
///////////////
 
76
// Constructor
 
77
FrameRate::FrameRate() {
 
78
        Unload();
 
79
}
 
80
 
 
81
 
 
82
//////////////
 
83
// Destructor
 
84
FrameRate::~FrameRate() {
 
85
        Unload();
 
86
}
 
87
 
 
88
 
 
89
//////////////////
 
90
// Loads VFR file
 
91
void FrameRate::Load(wxString filename) {
 
92
        using namespace std;
 
93
        
 
94
        Unload();
 
95
 
 
96
        // Check if file exists
 
97
        wxFileName filetest(filename);
 
98
        if (!filetest.FileExists()) throw _T("File not found.");
 
99
 
 
100
        // Open file
 
101
        TextFileReader file(filename);
 
102
 
 
103
        try {
 
104
                // Read header
 
105
                wxString curLine;
 
106
                curLine = file.ReadLineFromFile();
 
107
                wxString header = curLine;
 
108
                bool first = (header.Left(7).Lower() == _T("assume "));
 
109
 
 
110
                // V1, code converted from avcvfr9
 
111
                if (header == _T("# timecode format v1") || first) {
 
112
                        // Locate the default fps line
 
113
                        do {
 
114
                                // Get next line
 
115
                                if (!first) curLine = file.ReadLineFromFile();
 
116
                                first = false;
 
117
 
 
118
                                // Skip empty lines and comments
 
119
                                if (curLine == _T("") || curLine.Left(1) == _T("#")) continue;
 
120
 
 
121
                                else if (curLine.Left(7).Lower() != _T("assume ")) throw _T("Encountered data before 'Assume <fps>' line");
 
122
                                else {
 
123
                                        if (!curLine.Mid(6).ToDouble(&AverageFrameRate) || AverageFrameRate <= 0) throw _T("Invalid 'Assume <fps>' line");
 
124
                                        break;
 
125
                                }
 
126
                        } while (file.HasMoreLines());
 
127
 
 
128
                        // Read and expand all timecodes to v2
 
129
                        wxString curline;
 
130
 
 
131
                        double currenttime = 0;
 
132
                        int lposition = -1;
 
133
 
 
134
                        long lstart;
 
135
                        long lend;
 
136
                        double lfps;
 
137
 
 
138
                        while (file.HasMoreLines()) {
 
139
                                curLine = file.ReadLineFromFile();
 
140
 
 
141
                                // Skip empty lines and comments
 
142
                                if (curLine == _T("") || curLine.Left(1) == _T("#"))
 
143
                                        continue;
 
144
                                
 
145
                                wxString tmp = curLine.AfterFirst(_T(','));
 
146
                                wxString temp = curLine.BeforeFirst(_T(','));
 
147
                                if (!temp.ToLong(&lstart) || lstart < 0)
 
148
                                        throw _T("Timecode parsing error, invalid start format found");
 
149
                                temp = tmp.BeforeLast(_T(','));
 
150
                                if (!temp.ToLong(&lend) || lend < 0)
 
151
                                        throw _T("Timecode parsing error, invalid end format found");
 
152
                                temp = tmp.AfterLast(_T(','));
 
153
                                if (!temp.ToDouble(&lfps) || lfps <= 0)
 
154
                                        throw _T("Timecode parsing error, invalid fps format found");
 
155
 
 
156
                                if (lstart <= lposition)
 
157
                                        throw _T("Timecode parsing error, out of order or overlapping timecode range found"); 
 
158
 
 
159
 
 
160
                                for (int i = 0; i <= lstart - lposition - 2; i++)
 
161
                                        AddFrame((int)(floor(currenttime+(i*1000) / AverageFrameRate)));
 
162
 
 
163
                                currenttime += ((lstart - lposition - 1)*1000) / AverageFrameRate;
 
164
 
 
165
                                for (int i = 0; i <= lend - lstart; i++)
 
166
                                        AddFrame((int)(floor(currenttime+(i*1000) / lfps)));
 
167
 
 
168
                                currenttime += ((lend - lstart + 1)*1000) / lfps;
 
169
 
 
170
                                lposition = lend;
 
171
                        }
 
172
 
 
173
                        AddFrame(currenttime);
 
174
                        last_time = currenttime;
 
175
                        last_frame = (int)Frame.size() - 1;
 
176
                }
 
177
 
 
178
                // V2
 
179
                else if (header == _T("# timecode format v2")) {
 
180
                        // Assigns new VFR file
 
181
                        FrameRateType = VFR;
 
182
 
 
183
                        long lftime = -1;
 
184
                        long cftime = 0;
 
185
                        last_frame = 0;
 
186
 
 
187
                        // Reads body
 
188
                        while (file.HasMoreLines()) {
 
189
                                curLine = file.ReadLineFromFile();
 
190
 
 
191
                                //skip empty lines and comments
 
192
                                if (curLine == _T("") || curLine.Left(1) == _T("#"))
 
193
                                        continue;
 
194
 
 
195
                                wxString tmp = curLine.BeforeFirst(_T('.'));
 
196
                                tmp.ToLong(&cftime);
 
197
 
 
198
                                if (lftime >= cftime)
 
199
                                        throw _T("Out of order/too close timecodes found");
 
200
 
 
201
                                AddFrame(cftime);
 
202
                                lftime = cftime;
 
203
                        }
 
204
 
 
205
                        last_time = cftime;
 
206
                        last_frame = (int)Frame.size() - 1;
 
207
 
 
208
                        CalcAverage();
 
209
 
 
210
                }
 
211
 
 
212
                // Unknown
 
213
                else {
 
214
                        throw _T("Unknown time code file format.");
 
215
                }
 
216
 
 
217
        }
 
218
        catch (...) {
 
219
                Unload();
 
220
                throw;
 
221
        }
 
222
 
 
223
        // Close file
 
224
        loaded = true;
 
225
        vfrFile = filename;
 
226
        FrameRateType = VFR;
 
227
 
 
228
        // Add to recent
 
229
        Options.AddToRecentList(filename,_T("Recent timecodes"));
 
230
}
 
231
 
 
232
 
 
233
////////
 
234
// Save
 
235
void FrameRate::Save(wxString filename) {
 
236
        TextFileWriter file(filename,_T("ASCII"));
 
237
        file.WriteLineToFile(_T("# timecode format v2"));
 
238
        for (size_t i=0;i<Frame.size();i++) {
 
239
                file.WriteLineToFile(wxString::Format(_T("%f"),(float)Frame[i]));
 
240
        }
 
241
}
 
242
 
 
243
 
 
244
//////////
 
245
// Unload
 
246
void FrameRate::Unload () {
 
247
        FrameRateType = NONE;
 
248
        AverageFrameRate = 0;
 
249
        last_time = 0;
 
250
        last_frame = 0;
 
251
        Clear();
 
252
        loaded = false;
 
253
        vfrFile = _T("");
 
254
}
 
255
 
 
256
 
 
257
///////////////
 
258
// Sets to CFR
 
259
void FrameRate::SetCFR(double fps) {
 
260
        Unload();
 
261
        loaded = true;
 
262
        FrameRateType = CFR;
 
263
        AverageFrameRate = fps;
 
264
}
 
265
 
 
266
 
 
267
///////////////
 
268
// Sets to VFR
 
269
void FrameRate::SetVFR(std::vector<int> newTimes) {
 
270
        // Prepare
 
271
        Unload();
 
272
 
 
273
        loaded = true;
 
274
        FrameRateType = VFR;
 
275
 
 
276
        // Set new VFR;
 
277
        Frame = newTimes;
 
278
        CalcAverage();
 
279
        last_time = newTimes.back();
 
280
        last_frame = (int)newTimes.size();
 
281
}
 
282
 
 
283
 
 
284
/////////////////////////////
 
285
// Gets frame number at time
 
286
int FrameRate::PFrameAtTime(int ms,bool useceil) {
 
287
        // Check if it's loaded
 
288
        if (!loaded) return -1;
 
289
 
 
290
        // Normalize miliseconds
 
291
        ms = MAX(ms,0);
 
292
 
 
293
        // Get for constant frame rate
 
294
        if (FrameRateType == CFR || Frame.size() == 0) {
 
295
                double value = double(ms) * AverageFrameRate / 1000.0;
 
296
                if (useceil) return (int)ceil(value);
 
297
                else return (int)floor(value);
 
298
        }
 
299
 
 
300
        // Get for variable frame rate
 
301
        else if (FrameRateType == VFR) {
 
302
                // Get last
 
303
                double trueLast;
 
304
                //if (useceil) trueLast = ceil(last_time);
 
305
                //else trueLast = floor(last_time);
 
306
                trueLast = Frame[Frame.size()-1];
 
307
 
 
308
                // Inside VFR range
 
309
                if (ms <= trueLast) {
 
310
                        // Prepare binary search
 
311
                        size_t start = 0;
 
312
                        size_t end = last_frame;
 
313
                        size_t cur;
 
314
                        bool largerEqual;
 
315
 
 
316
                        // Do binary search
 
317
                        while (start <= end) {
 
318
                                // Current frame being checked
 
319
                                cur = (start+end)>>1;
 
320
 
 
321
                                // Is it larger or equal to searched time?
 
322
                                largerEqual = Frame[cur] >= ms;
 
323
 
 
324
                                // If it is, is the previous smaller?
 
325
                                // If so, this is the frame we're looking for
 
326
                                if (largerEqual && (cur == 0 || Frame[cur-1] < ms))     {
 
327
                                        if (useceil) return (int)cur;
 
328
                                        return (int)(cur)-1;
 
329
                                }
 
330
                                
 
331
                                // Not found, continue search
 
332
                                if (largerEqual) end = cur-1;
 
333
                                else start = cur+1;
 
334
                        }
 
335
                }
 
336
                
 
337
                // After VFR range
 
338
                else {
 
339
                        if (useceil) return (int)(last_frame + ceil((ms-last_time) * AverageFrameRate / 1000));
 
340
                        else return (int)(last_frame + floor((ms-last_time) * AverageFrameRate / 1000));
 
341
                }
 
342
        }
 
343
        return -1;
 
344
}
 
345
 
 
346
 
 
347
//////////////////////
 
348
// Gets time at frame
 
349
int FrameRate::PTimeAtFrame(int frame) {
 
350
        // Not loaded
 
351
        if (!loaded) return -1;
 
352
 
 
353
        // For negative/zero times, fallback to zero
 
354
        if (frame <= 0) return 0;
 
355
 
 
356
        // Constant frame rate
 
357
        if (FrameRateType == CFR || Frame.size() == 0) {
 
358
                return (int)floor(double(frame) / AverageFrameRate * 1000.0);
 
359
        }
 
360
        
 
361
        // Variable frame rate
 
362
        else if (FrameRateType == VFR) {
 
363
                // Is it inside frame rate range? If so, just get the value from timecodes table
 
364
                if (frame < (signed) Frame.size()) return Frame.at(frame);
 
365
 
 
366
                // Otherwise, calculate it
 
367
                else return (int)floor(Frame.back() + double(frame-Frame.size()+1) / AverageFrameRate * 1000.0);
 
368
        }
 
369
 
 
370
        // Unknown frame rate type
 
371
        return -1;
 
372
}
 
373
 
 
374
 
 
375
/////////////////////////////
 
376
// Get correct frame at time
 
377
// returns the adjusted time for end frames when start=false
 
378
// otherwise for start frames
 
379
int FrameRate::GetFrameAtTime(int ms,bool start) {
 
380
        return PFrameAtTime(ms,start);
 
381
}
 
382
 
 
383
 
 
384
/////////////////////////////
 
385
// Get correct time at frame
 
386
// compensates and returns an end time when start=false
 
387
int FrameRate::GetTimeAtFrame(int frame,bool start,bool exact) {
 
388
        int finalTime;
 
389
 
 
390
        // Exact, for display
 
391
        if (exact) {
 
392
                finalTime = PTimeAtFrame(frame);
 
393
        }
 
394
 
 
395
        // Adjusted, for subs sync
 
396
        else {
 
397
                if (start) {
 
398
                        finalTime = (PTimeAtFrame(frame-1) + PTimeAtFrame(frame))/2;
 
399
                }
 
400
                else {
 
401
                        //if (FrameRateType == VFR) finalTime = PTimeAtFrame(frame);
 
402
                        //else finalTime = (PTimeAtFrame(frame) + PTimeAtFrame(frame+1))/2;
 
403
                        finalTime = (PTimeAtFrame(frame) + PTimeAtFrame(frame+1))/2;
 
404
                }
 
405
        }
 
406
 
 
407
        return finalTime;
 
408
}
 
409
 
 
410
 
 
411
////////////////////////////////////////
 
412
// Get the current list of frames/times
 
413
std::vector<int> FrameRate::GetFrameTimeList() {
 
414
        return Frame;
 
415
}
 
416
 
 
417
 
 
418
///////////////////////////////////////////
 
419
// Calculate the common FPS for evil stuff
 
420
// e.g., in a mix of 24fps and 30fps, returns 120fps
 
421
double FrameRate::GetCommonFPS() {
 
422
        // Variables
 
423
        int curDist;
 
424
        int lastDist = 0;
 
425
        int sectionStart = 0;
 
426
        double curFps;
 
427
 
 
428
        // List of likely frame rates
 
429
        std::vector<double> frameRates;
 
430
        frameRates.push_back(15.0 / 1.001);
 
431
        frameRates.push_back(15);
 
432
        frameRates.push_back(24.0 / 1.001);
 
433
        frameRates.push_back(24);
 
434
        frameRates.push_back(30.0 / 1.001);
 
435
        frameRates.push_back(30);
 
436
        frameRates.push_back(120.0 / 1.001);
 
437
        frameRates.push_back(120);
 
438
 
 
439
        // List of rates found
 
440
        std::vector<double> found;
 
441
 
 
442
        // Find the relative fps of each area
 
443
        for (unsigned int i=1;i<Frame.size();i++) {
 
444
                // Find the current frame distance
 
445
                curDist = Frame[i]-Frame[i-1];
 
446
 
 
447
                // See if it's close enough to the last
 
448
                if ((abs(curDist - lastDist) < 2 || i-1 == (unsigned) sectionStart) && i != Frame.size()-1) {
 
449
                        lastDist = curDist;
 
450
                        continue;
 
451
                }
 
452
 
 
453
                // Calculate section fps
 
454
                curFps = (i - sectionStart - 1) * 1000.0 / double(Frame[i-1]-Frame[sectionStart]);
 
455
                sectionStart = i;
 
456
                lastDist = curDist;
 
457
 
 
458
                // See if it's close enough to one of the likely rates
 
459
                for (unsigned int j=0;j<frameRates.size();j++) {
 
460
                        if (curFps-0.01 <= frameRates[j] && curFps+0.01 >= frameRates[j]) {
 
461
                                curFps = frameRates[j];
 
462
                                break;
 
463
                        }
 
464
                }
 
465
 
 
466
                // See if it's on list
 
467
                bool onList = false;
 
468
                for (unsigned int j=0;j<found.size();j++) {
 
469
                        if (found[j] == curFps) {
 
470
                                onList = true;
 
471
                                break;
 
472
                        }
 
473
                }
 
474
 
 
475
                // If not, add it
 
476
                if (!onList) found.push_back(curFps);
 
477
        }
 
478
 
 
479
        // Find common between them
 
480
        double v1,v2,minInt,tempd;
 
481
        int tempi1,tempi2;
 
482
        while (found.size() > 1) {
 
483
                // Extract last two values
 
484
                v1 = found.back();
 
485
                found.pop_back();
 
486
                v2 = found.back();
 
487
                found.pop_back();
 
488
 
 
489
                // Divide them
 
490
                v2 = v1/v2;
 
491
 
 
492
                // Find what it takes to make it an integer
 
493
                for (minInt = 1;minInt<20;minInt++) {
 
494
                        tempd = v2 * minInt;
 
495
                        tempi1 = (int)(tempd-0.001);
 
496
                        tempi2 = (int)(tempd+0.001);
 
497
                        if (tempi1 != tempi2) break;
 
498
                }
 
499
                if (minInt != 20) v1 = v1*minInt;
 
500
 
 
501
                // See if it's close enough to one of the likely rates
 
502
                for (unsigned int j=0;j<frameRates.size();j++) {
 
503
                        if (v1-0.01 <= frameRates[j] && v1+0.01 >= frameRates[j]) {
 
504
                                v1 = frameRates[j];
 
505
                                break;
 
506
                        }
 
507
                }
 
508
 
 
509
                // Re-insert obtained result
 
510
                found.push_back(v1);
 
511
        }
 
512
 
 
513
        return found.back();
 
514
}
 
515
 
 
516
 
 
517
///////////
 
518
// Globals
 
519
FrameRate VFR_Output;
 
520
FrameRate VFR_Input;