Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
52867 views
1
/*****************************************************************************
2
* timecode.c: timecode file input
3
*****************************************************************************
4
* Copyright (C) 2010-2016 x264 project
5
*
6
* Authors: Yusuke Nakamura <[email protected]>
7
*
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation; either version 2 of the License, or
11
* (at your option) any later version.
12
*
13
* This program is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
17
*
18
* You should have received a copy of the GNU General Public License
19
* along with this program; if not, write to the Free Software
20
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
21
*
22
* This program is also available under a commercial proprietary license.
23
* For more information, contact us at [email protected].
24
*****************************************************************************/
25
26
#include "input.h"
27
#define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, "timecode", __VA_ARGS__ )
28
29
typedef struct
30
{
31
cli_input_t input;
32
hnd_t p_handle;
33
int auto_timebase_num;
34
int auto_timebase_den;
35
uint64_t timebase_num;
36
uint64_t timebase_den;
37
int stored_pts_num;
38
int64_t *pts;
39
double assume_fps;
40
double last_timecode;
41
} timecode_hnd_t;
42
43
static inline double sigexp10( double value, double *exponent )
44
{
45
/* This function separates significand and exp10 from double floating point. */
46
*exponent = pow( 10, floor( log10( value ) ) );
47
return value / *exponent;
48
}
49
50
#define DOUBLE_EPSILON 5e-6
51
#define MKV_TIMEBASE_DEN 1000000000
52
53
static double correct_fps( double fps, timecode_hnd_t *h )
54
{
55
int i = 1;
56
uint64_t fps_num, fps_den;
57
double exponent;
58
double fps_sig = sigexp10( fps, &exponent );
59
while( 1 )
60
{
61
fps_den = i * h->timebase_num;
62
fps_num = round( fps_den * fps_sig ) * exponent;
63
FAIL_IF_ERROR( fps_num > UINT32_MAX, "tcfile fps correction failed.\n"
64
" Specify an appropriate timebase manually or remake tcfile.\n" )
65
if( fabs( ((double)fps_num / fps_den) / exponent - fps_sig ) < DOUBLE_EPSILON )
66
break;
67
++i;
68
}
69
if( h->auto_timebase_den )
70
{
71
h->timebase_den = h->timebase_den ? lcm( h->timebase_den, fps_num ) : fps_num;
72
if( h->timebase_den > UINT32_MAX )
73
h->auto_timebase_den = 0;
74
}
75
return (double)fps_num / fps_den;
76
}
77
78
static int try_mkv_timebase_den( double *fpss, timecode_hnd_t *h, int loop_num )
79
{
80
h->timebase_num = 0;
81
h->timebase_den = MKV_TIMEBASE_DEN;
82
for( int num = 0; num < loop_num; num++ )
83
{
84
uint64_t fps_den;
85
double exponent;
86
double fps_sig = sigexp10( fpss[num], &exponent );
87
fps_den = round( MKV_TIMEBASE_DEN / fps_sig ) / exponent;
88
h->timebase_num = fps_den && h->timebase_num ? gcd( h->timebase_num, fps_den ) : fps_den;
89
FAIL_IF_ERROR( h->timebase_num > UINT32_MAX || !h->timebase_num, "automatic timebase generation failed.\n"
90
" Specify timebase manually.\n" )
91
}
92
return 0;
93
}
94
95
static int parse_tcfile( FILE *tcfile_in, timecode_hnd_t *h, video_info_t *info )
96
{
97
char buff[256];
98
int ret, tcfv, num, seq_num, timecodes_num;
99
double *timecodes = NULL;
100
double *fpss = NULL;
101
102
ret = fscanf( tcfile_in, "# timecode format v%d", &tcfv );
103
FAIL_IF_ERROR( ret != 1 || (tcfv != 1 && tcfv != 2), "unsupported timecode format\n" )
104
#define NO_TIMECODE_LINE (buff[0] == '#' || buff[0] == '\n' || buff[0] == '\r')
105
if( tcfv == 1 )
106
{
107
uint64_t file_pos;
108
double assume_fps, seq_fps;
109
int start, end = -1;
110
int prev_start = -1, prev_end = -1;
111
112
h->assume_fps = 0;
113
for( num = 2; fgets( buff, sizeof(buff), tcfile_in ) != NULL; num++ )
114
{
115
if( NO_TIMECODE_LINE )
116
continue;
117
FAIL_IF_ERROR( sscanf( buff, "assume %lf", &h->assume_fps ) != 1 && sscanf( buff, "Assume %lf", &h->assume_fps ) != 1,
118
"tcfile parsing error: assumed fps not found\n" )
119
break;
120
}
121
FAIL_IF_ERROR( h->assume_fps <= 0, "invalid assumed fps %.6f\n", h->assume_fps )
122
123
file_pos = ftell( tcfile_in );
124
h->stored_pts_num = 0;
125
for( seq_num = 0; fgets( buff, sizeof(buff), tcfile_in ) != NULL; num++ )
126
{
127
if( NO_TIMECODE_LINE )
128
{
129
if( sscanf( buff, "# TDecimate Mode 3: Last Frame = %d", &end ) == 1 )
130
h->stored_pts_num = end + 1;
131
continue;
132
}
133
ret = sscanf( buff, "%d,%d,%lf", &start, &end, &seq_fps );
134
FAIL_IF_ERROR( ret != 3 && ret != EOF, "invalid input tcfile\n" )
135
FAIL_IF_ERROR( start > end || start <= prev_start || end <= prev_end || seq_fps <= 0,
136
"invalid input tcfile at line %d: %s\n", num, buff )
137
prev_start = start;
138
prev_end = end;
139
if( h->auto_timebase_den || h->auto_timebase_num )
140
++seq_num;
141
}
142
if( !h->stored_pts_num )
143
h->stored_pts_num = end + 2;
144
timecodes_num = h->stored_pts_num;
145
fseek( tcfile_in, file_pos, SEEK_SET );
146
147
timecodes = malloc( timecodes_num * sizeof(double) );
148
if( !timecodes )
149
return -1;
150
if( h->auto_timebase_den || h->auto_timebase_num )
151
{
152
fpss = malloc( (seq_num + 1) * sizeof(double) );
153
if( !fpss )
154
goto fail;
155
}
156
157
assume_fps = correct_fps( h->assume_fps, h );
158
if( assume_fps < 0 )
159
goto fail;
160
timecodes[0] = 0;
161
for( num = seq_num = 0; num < timecodes_num - 1 && fgets( buff, sizeof(buff), tcfile_in ) != NULL; )
162
{
163
if( NO_TIMECODE_LINE )
164
continue;
165
ret = sscanf( buff, "%d,%d,%lf", &start, &end, &seq_fps );
166
if( ret != 3 )
167
start = end = timecodes_num - 1;
168
for( ; num < start && num < timecodes_num - 1; num++ )
169
timecodes[num + 1] = timecodes[num] + 1 / assume_fps;
170
if( num < timecodes_num - 1 )
171
{
172
if( h->auto_timebase_den || h->auto_timebase_num )
173
fpss[seq_num++] = seq_fps;
174
seq_fps = correct_fps( seq_fps, h );
175
if( seq_fps < 0 )
176
goto fail;
177
for( num = start; num <= end && num < timecodes_num - 1; num++ )
178
timecodes[num + 1] = timecodes[num] + 1 / seq_fps;
179
}
180
}
181
for( ; num < timecodes_num - 1; num++ )
182
timecodes[num + 1] = timecodes[num] + 1 / assume_fps;
183
if( h->auto_timebase_den || h->auto_timebase_num )
184
fpss[seq_num] = h->assume_fps;
185
186
if( h->auto_timebase_num && !h->auto_timebase_den )
187
{
188
double exponent;
189
double assume_fps_sig, seq_fps_sig;
190
if( try_mkv_timebase_den( fpss, h, seq_num + 1 ) < 0 )
191
goto fail;
192
fseek( tcfile_in, file_pos, SEEK_SET );
193
assume_fps_sig = sigexp10( h->assume_fps, &exponent );
194
assume_fps = MKV_TIMEBASE_DEN / ( round( MKV_TIMEBASE_DEN / assume_fps_sig ) / exponent );
195
for( num = 0; num < timecodes_num - 1 && fgets( buff, sizeof(buff), tcfile_in ) != NULL; )
196
{
197
if( NO_TIMECODE_LINE )
198
continue;
199
ret = sscanf( buff, "%d,%d,%lf", &start, &end, &seq_fps );
200
if( ret != 3 )
201
start = end = timecodes_num - 1;
202
seq_fps_sig = sigexp10( seq_fps, &exponent );
203
seq_fps = MKV_TIMEBASE_DEN / ( round( MKV_TIMEBASE_DEN / seq_fps_sig ) / exponent );
204
for( ; num < start && num < timecodes_num - 1; num++ )
205
timecodes[num + 1] = timecodes[num] + 1 / assume_fps;
206
for( num = start; num <= end && num < timecodes_num - 1; num++ )
207
timecodes[num + 1] = timecodes[num] + 1 / seq_fps;
208
}
209
for( ; num < timecodes_num - 1; num++ )
210
timecodes[num + 1] = timecodes[num] + 1 / assume_fps;
211
}
212
if( fpss )
213
{
214
free( fpss );
215
fpss = NULL;
216
}
217
218
h->assume_fps = assume_fps;
219
h->last_timecode = timecodes[timecodes_num - 1];
220
}
221
else /* tcfv == 2 */
222
{
223
uint64_t file_pos = ftell( tcfile_in );
224
225
h->stored_pts_num = 0;
226
while( fgets( buff, sizeof(buff), tcfile_in ) != NULL )
227
{
228
if( NO_TIMECODE_LINE )
229
{
230
if( !h->stored_pts_num )
231
file_pos = ftell( tcfile_in );
232
continue;
233
}
234
h->stored_pts_num++;
235
}
236
timecodes_num = h->stored_pts_num;
237
FAIL_IF_ERROR( !timecodes_num, "input tcfile doesn't have any timecodes!\n" )
238
fseek( tcfile_in, file_pos, SEEK_SET );
239
240
timecodes = malloc( timecodes_num * sizeof(double) );
241
if( !timecodes )
242
return -1;
243
244
num = 0;
245
if( fgets( buff, sizeof(buff), tcfile_in ) != NULL )
246
{
247
ret = sscanf( buff, "%lf", &timecodes[0] );
248
timecodes[0] *= 1e-3; /* Timecode format v2 is expressed in milliseconds. */
249
FAIL_IF_ERROR( ret != 1, "invalid input tcfile for frame 0\n" )
250
for( num = 1; num < timecodes_num && fgets( buff, sizeof(buff), tcfile_in ) != NULL; )
251
{
252
if( NO_TIMECODE_LINE )
253
continue;
254
ret = sscanf( buff, "%lf", &timecodes[num] );
255
timecodes[num] *= 1e-3; /* Timecode format v2 is expressed in milliseconds. */
256
FAIL_IF_ERROR( ret != 1 || timecodes[num] <= timecodes[num - 1],
257
"invalid input tcfile for frame %d\n", num )
258
++num;
259
}
260
}
261
FAIL_IF_ERROR( num < timecodes_num, "failed to read input tcfile for frame %d", num )
262
263
if( timecodes_num == 1 )
264
h->timebase_den = info->fps_num;
265
else if( h->auto_timebase_den )
266
{
267
fpss = malloc( (timecodes_num - 1) * sizeof(double) );
268
if( !fpss )
269
goto fail;
270
for( num = 0; num < timecodes_num - 1; num++ )
271
{
272
fpss[num] = 1 / (timecodes[num + 1] - timecodes[num]);
273
if( h->auto_timebase_den )
274
{
275
int i = 1;
276
uint64_t fps_num, fps_den;
277
double exponent;
278
double fps_sig = sigexp10( fpss[num], &exponent );
279
while( 1 )
280
{
281
fps_den = i * h->timebase_num;
282
fps_num = round( fps_den * fps_sig ) * exponent;
283
if( fps_num > UINT32_MAX || fabs( ((double)fps_num / fps_den) / exponent - fps_sig ) < DOUBLE_EPSILON )
284
break;
285
++i;
286
}
287
h->timebase_den = fps_num && h->timebase_den ? lcm( h->timebase_den, fps_num ) : fps_num;
288
if( h->timebase_den > UINT32_MAX )
289
{
290
h->auto_timebase_den = 0;
291
continue;
292
}
293
}
294
}
295
if( h->auto_timebase_num && !h->auto_timebase_den )
296
if( try_mkv_timebase_den( fpss, h, timecodes_num - 1 ) < 0 )
297
goto fail;
298
free( fpss );
299
fpss = NULL;
300
}
301
302
if( timecodes_num > 1 )
303
h->assume_fps = 1 / (timecodes[timecodes_num - 1] - timecodes[timecodes_num - 2]);
304
else
305
h->assume_fps = (double)info->fps_num / info->fps_den;
306
h->last_timecode = timecodes[timecodes_num - 1];
307
}
308
#undef NO_TIMECODE_LINE
309
if( h->auto_timebase_den || h->auto_timebase_num )
310
{
311
uint64_t i = gcd( h->timebase_num, h->timebase_den );
312
h->timebase_num /= i;
313
h->timebase_den /= i;
314
x264_cli_log( "timecode", X264_LOG_INFO, "automatic timebase generation %"PRIu64"/%"PRIu64"\n", h->timebase_num, h->timebase_den );
315
}
316
else FAIL_IF_ERROR( h->timebase_den > UINT32_MAX || !h->timebase_den, "automatic timebase generation failed.\n"
317
" Specify an appropriate timebase manually.\n" )
318
319
h->pts = malloc( h->stored_pts_num * sizeof(int64_t) );
320
if( !h->pts )
321
goto fail;
322
for( num = 0; num < h->stored_pts_num; num++ )
323
{
324
h->pts[num] = timecodes[num] * ((double)h->timebase_den / h->timebase_num) + 0.5;
325
FAIL_IF_ERROR( num > 0 && h->pts[num] <= h->pts[num - 1], "invalid timebase or timecode for frame %d\n", num )
326
}
327
328
free( timecodes );
329
return 0;
330
331
fail:
332
if( timecodes )
333
free( timecodes );
334
if( fpss )
335
free( fpss );
336
return -1;
337
}
338
339
#undef DOUBLE_EPSILON
340
#undef MKV_TIMEBASE_DEN
341
342
static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt )
343
{
344
int ret = 0;
345
FILE *tcfile_in;
346
timecode_hnd_t *h = malloc( sizeof(timecode_hnd_t) );
347
FAIL_IF_ERROR( !h, "malloc failed\n" )
348
h->input = cli_input;
349
h->p_handle = *p_handle;
350
h->pts = NULL;
351
if( opt->timebase )
352
{
353
ret = sscanf( opt->timebase, "%"SCNu64"/%"SCNu64, &h->timebase_num, &h->timebase_den );
354
if( ret == 1 )
355
{
356
h->timebase_num = strtoul( opt->timebase, NULL, 10 );
357
h->timebase_den = 0; /* set later by auto timebase generation */
358
}
359
FAIL_IF_ERROR( h->timebase_num > UINT32_MAX || h->timebase_den > UINT32_MAX,
360
"timebase you specified exceeds H.264 maximum\n" )
361
}
362
h->auto_timebase_num = !ret;
363
h->auto_timebase_den = ret < 2;
364
if( h->auto_timebase_num )
365
h->timebase_num = info->fps_den; /* can be changed later by auto timebase generation */
366
if( h->auto_timebase_den )
367
h->timebase_den = 0; /* set later by auto timebase generation */
368
369
tcfile_in = x264_fopen( psz_filename, "rb" );
370
FAIL_IF_ERROR( !tcfile_in, "can't open `%s'\n", psz_filename )
371
else if( !x264_is_regular_file( tcfile_in ) )
372
{
373
x264_cli_log( "timecode", X264_LOG_ERROR, "tcfile input incompatible with non-regular file `%s'\n", psz_filename );
374
fclose( tcfile_in );
375
return -1;
376
}
377
378
if( parse_tcfile( tcfile_in, h, info ) < 0 )
379
{
380
if( h->pts )
381
free( h->pts );
382
fclose( tcfile_in );
383
return -1;
384
}
385
fclose( tcfile_in );
386
387
info->timebase_num = h->timebase_num;
388
info->timebase_den = h->timebase_den;
389
info->vfr = 1;
390
391
*p_handle = h;
392
return 0;
393
}
394
395
static int64_t get_frame_pts( timecode_hnd_t *h, int frame, int real_frame )
396
{
397
if( frame < h->stored_pts_num )
398
return h->pts[frame];
399
else
400
{
401
if( h->pts && real_frame )
402
{
403
x264_cli_log( "timecode", X264_LOG_INFO, "input timecode file missing data for frame %d and later\n"
404
" assuming constant fps %.6f\n", frame, h->assume_fps );
405
free( h->pts );
406
h->pts = NULL;
407
}
408
double timecode = h->last_timecode + 1 / h->assume_fps;
409
if( real_frame )
410
h->last_timecode = timecode;
411
return timecode * ((double)h->timebase_den / h->timebase_num) + 0.5;
412
}
413
}
414
415
static int read_frame( cli_pic_t *pic, hnd_t handle, int frame )
416
{
417
timecode_hnd_t *h = handle;
418
if( h->input.read_frame( pic, h->p_handle, frame ) )
419
return -1;
420
421
pic->pts = get_frame_pts( h, frame, 1 );
422
pic->duration = get_frame_pts( h, frame + 1, 0 ) - pic->pts;
423
424
return 0;
425
}
426
427
static int release_frame( cli_pic_t *pic, hnd_t handle )
428
{
429
timecode_hnd_t *h = handle;
430
if( h->input.release_frame )
431
return h->input.release_frame( pic, h->p_handle );
432
return 0;
433
}
434
435
static int picture_alloc( cli_pic_t *pic, hnd_t handle, int csp, int width, int height )
436
{
437
timecode_hnd_t *h = handle;
438
return h->input.picture_alloc( pic, h->p_handle, csp, width, height );
439
}
440
441
static void picture_clean( cli_pic_t *pic, hnd_t handle )
442
{
443
timecode_hnd_t *h = handle;
444
h->input.picture_clean( pic, h->p_handle );
445
}
446
447
static int close_file( hnd_t handle )
448
{
449
timecode_hnd_t *h = handle;
450
if( h->pts )
451
free( h->pts );
452
h->input.close_file( h->p_handle );
453
free( h );
454
return 0;
455
}
456
457
const cli_input_t timecode_input = { open_file, picture_alloc, read_frame, release_frame, picture_clean, close_file };
458
459