[Matroska-devel] timecode scale handling & sample precision
m.bunkus at linet-services.de
Sun Aug 1 13:51:17 CEST 2004
until now I really didn't care all that much, but now that I'm
implementing sample precision I have to care. So I took the time to
study the current explanation for the timecode scale handling available
It is wrong.
Let's take the sample rate of 44100. A timecode scale parameter of
1000000000/44100 is 22675.736961. The example uses 22676 which results
in a precision that is LESS than the sample rate. There's no way to to
regenerate 44100 different sample numbers from < 44100 different values.
It must be 22675 - always _truncate_ the timecode scale factor.
The next thing is:
To determine which sample that is,
(313450348 / 1000000000) * 44100 = ~13823.16
Check to see if the value is greater than the truncated version of
itself and if so, round up.
A value is always at least as big as its truncated value! So this will
simply result in wrong values. You're throwing away up to nearly 1
sample of time. There's NO way a program can regenerate that from the
raw timecode and the timecode scale factor.
Here's how to use it right including a small demonstration application:
(I'll use $var for a variable name because the text might be confusing
Use the following timecode scale value:
$tc_scale = (int64_t)(1000000000ll / $sample_rate)
Let's assume we have a sample number $sample, the sample rate
$sample_rate and the timecode scale factor as given above. We further
need a function that can round a value to the nearest integer (in this
case the nearest 64bit integer). We can use a simple #define for this:
#define irnd(v) ((int64_t)((v) + 0.5) > (int64_t)(v) ? \
(int64_t)((v) + 0.5) : (int64_t)(v))
The 'original' timecode $unscaled_timecode is the one the application
deals with. It is obviously just
$unscaled_timecode = 1000000000ll * $sample / $sample_rate
Easy enough. Even better wound be using double precision and rounding,
but 1ns is WAY below any sensible $sample_rate so we can live with the
imprecision of just truncating this value to the lower ns.
Next: the $scaled_timecode is the one that is actually written to the
Matroska file. Other folks call this the 'raw' timecode. I'll use
'scaled' for timecodes whose unit is '1 $tc_scale' and 'unscaled' for
timecodes whose unit is '1ns'.
--> Here we HAVE to round! <--
This timecode is
$scaled_timecode = irnd((double)$unscaled_timecode / (double)$tc_scale)
Now we have a nice scaled timecode in our file. Another application
wants to read such a file, gets $tc_scale from the header and reads a
block. Inside that file it finds $scaled_timecode for that block. From
that it can calculated the 'unscaled' timecode on the reader's part:
$rescaled_timecode = $scaled_timecode * $tc_scale
Easy. No rounding needed obviously because we multiply integers in the
first place. How does the application get the sample number from this?
Again this involves...
--> ROUNGIN! <--
$recalculated_sample = irnd((double)$rescaled_timecode * $sample_rate /
Et voila. That's it.
I've attached a small test application that demonstrates this for a
sample rate of 44100. It will output each of the four values along with
a string 'ok' or 'NOT OK!' if the $sample is equal to or different from
the $recalculated_sample. If you don't believe me just test it, redirect
the output to a file and search for 'NOT OK'. You won't find it.
I REALLY hope I'm not making a mistake here, but I'm pretty sure I
don't. So I'll overhaul the web page mentioned at the beginning with
this information and modify mkvmerge accordingly if no one objects.
Bunkus, Geisler und Reetz GbR
Gotenweg 15 Tel.: 0531-280 191 71
38106 Braunschweig Fax.: 0531-280 191 72
mailto:info at linet-services.de
-------------- next part --------------
A non-text attachment was scrubbed...
Size: 1180 bytes
Desc: not available
More information about the Matroska-devel