This was originally posted on my old blog in 2010. In redesigning and updating my site, I’ve been able to revise the post and clear up a few issues, particularly around 23.976 drop-frame timecode.
In this post, I’m going to include some pseudocode for converting between drop-frame timecode and a frame number and vice versa. You can skip to that if you want. Or, stay tuned for a little bit of background info.

About Drop-Frame Timecode

In order to do math with timecode, which is in an hours:minutes:seconds:frames format, it must be converted into a number representing the total number of frames. You then do your math with frame numbers, and convert back to the timecode format. For non-drop-frame timecode, this is trivial (I’ve included that code below as well for completeness). It gets complicated when you’re dealing with drop-frame timecode. If you’re new to drop-frame, it’s a way of accurately measuring the running time of video that doesn’t run at an even frame rate. In the United States and several other countries, video runs at 29.97 frames per second, but we usually count it at 30 fps. This means that, after a while, the timecode does not accurately reflect the running time of the video. For an hour-long program, the timecode gets to be about 3.6 seconds off. The solution to this problem is drop-frame timecode, which is sort of like leap year for timecode. At 29.97 fps, every minute (except minutes divisible by ten), you skip counting the first two frames. You know you’re looking at drop frame timecode because the colon between minutes and frames is usually replaced by a semicolon. For example, you go:

  • 12:38:59;27
  • 12:38:59;28
  • 12:38:59;29
  • and then you skip to…
  • 12:39:00;02
  • 12:39:00;03
It’s difficult to calculate all of this if you’re given a frame number, or to calculate a frame number if you’re given this. After a lot of searching, I’ve only been able to come up with one website that sets out a formula for how to convert between frames and drop frame timecode, Andrew Duncan’s excellent timecode page. The code I’m about to post is more or less borrowed straight from that site (with Andrew’s permission). I’m really making only two changes. First, I’m expanding a couple steps to make it a little more like a computer program, and second, I’m generalizing it so it can calculate drop-frame timecode for 59.94 as well. For 59.94DF, you drop the first four frames per minute. In my original post on this subject, I said that this code will work for 23.976 as well. After some reflection, this is not the case. There is no such thing as 23.976 drop-frame timecode, for good reasons, which I’ll explain in further detail after the code snippets below. So, what I’m presenting here are basically Andrew’s formulas, fleshed out a little bit, and generalized.

Symbols

I use a few symbols in the code below that may require a word of explanation:
  • I’m using the \ symbol to represent integer division. In (positive) integer division, we divide two numbers and discard the decimal part. So 3 \ 2 would just be equal to 1. Different languages represent integer division in different ways. Java and C will do it automatically as long as you’re dividing integer types. Javascript requires taking Math.floor() of regular division. Python 2.x will also do it automatically with integer types, but it can be forced to do it with a / / symbol, which is required in Python 3.x. Ruby will also do it automatically with integers and it can be forced with Numeric#div. It looks like \ the operator comes from the BASIC world. Different languages do different things when doing integer division with negative numbers. Some round towards zero, others round down. That’s beyond the scope of this tutorial, though.
  • I’m using the symbol % to represent the modulo operator. Modulo refers to the remainder that’s left after division. 30 / 7 is 4 with a remainder of 2, so 30 % 7 would be 2. Most languages use the same % notation for modulo. As with integer division, modulo behaviors vary among programming languages when negative numbers are involved. Again, we’re not going to worry about those distinctions here.

Finally, the Code!

So, without further ado, here’s some pseudocode in a vaguely Java style. First, we’ll convert a frame number into hours, minutes, seconds, and frames.

[This code has been updated on 8/8/2010 to correct a bug on line 27 caught by Jean-Baptiste Mardelle of Kdenlive. It was updated again on 1/4/2012 to fix a minor syntax error and correct a display issue with < and > symbols. Thanks to Martin Baker for catching those. I updated it in October, 2019 to remove 24p and to address an issue related to negative values]

Frame Number to Drop-Frame Timecode

Copy to Clipboard

Drop-Frame Timecode to Frame Number

Copy to Clipboard

Non-Drop-Frame Timecode

For the sake of completeness, here’s the code for the relatively trivial task of converting between frame numbers and non-drop-frame timecode. This code is general and will work for any frame rate including 23.976p.

Frame Number to NDF

Copy to Clipboard

NDF to Frame Number

Copy to Clipboard

A Note About 24p

In NTSC countries, 24p footage actually runs at 23.976 fps. 23.976 drop-frame timecode is not part of the SMPTE timecode spec and most editing programs don’t have a 24p drop-frame option. There are good reasons for this. Let’s think about how drop-frame timecode works for 29.97. Television programs commonly run in 30 minute increments, so drop-frame timecode should work in such a way that it is guaranteed to be a correct representation of the running time at least every 30 minutes. In fact, drop-frame is cyclical every ten minutes and, therefore, is guaranteed to accurately represent run-time every ten minutes.

At 30 frames per second, to convert ten minutes of programming to a frame count, you multiply:
30 (frames per second) * 60 (seconds per minute) * 10 (minutes) = 18,000

However, since NTSC video operates at 29.97, not 30 frames per second, the actual number of frames in ten minutes of NTSC video is:
29.97 (frames per second) * 60 (seconds per minute) * 10 (minutes) = 17,982

That means that every ten minutes, drop-frame timecode needs to make up for the discrepancy between 18,000 frames and 17,982 frames, which is 18 frames. And that’s exactly what drop-frame timecode does. By skipping two frames every minute, except for minutes divisible by ten, the timecode skips exactly 18 frames every ten minutes and therefore, even if drop-frame timecode may deviate from actual running time by a frame or two within a ten minute cycle, at the end of that cycle, it is guaranteed to be consistent.

Now let’s run those same numbers for 23.976:

24 (frames per second) * 60 (seconds per minute) * 10 (minutes) = 14,400
23.976 (frames per second) * 60 (seconds per minute) * 10 (minutes) = 14,385.6

Already, we can see that we might be in trouble here. The discrepancy over ten minutes is 14.4 frames, which has a decimal component. Even at 30 minutes or 60 minutes, the discrepancy between 24 and 23.976 has a decimal. That means that no matter what rules you set for dropping frames, there’s no way to make it come out evenly over a 60 minute period. There’s actually no way to make it come out evenly even over a 24 hour period. There’s simply no way to devise a formula to accurately do drop-frame for 23.976 footage.

The issue here is that 23.976 has more significant digits than 29.97.* Over a ten minute period, you’re multiplying your frame rate by 600 (60 seconds per minute * 10 minutes). That’s enough to multiply the 0.97 part of 29.97 into an integer, but at 23.976, you’re still stuck with that pesky 0.6.

So what should you do if you have 23.976 footage? Well, below is some code to get accurate timings, at least. We’ll call it pseudo-drop-frame for 24p. This is not part of the SMPTE spec, but it will give you a good estimate of the actual running time of your program.

Basically, what we’re doing here is calculating the true runtime in seconds and then using the remaining fractions of a second to calculate frames.

Practically, this means that a 30 minute 24p program should be (about) 43,157 frames long and a 60 minute program should be (about) 86,314. That translates to 24p non drop-frame timecode of 00:29:58:05 and 00:59:56:10 respectively.

Frame Number to Pseudo-Drop-Frame 24p

Copy to Clipboard

Pseudo-Drop-Frame 24p to Frame Number

Copy to Clipboard
* – Okay, okay. Some readers may have noted that this entire post is built upon a lie! A minor lie, but one that we should talk about. All of the above is premised on the notion that NTSC video is 29.97 frames per second. But that’s not entirely true. NTSC video is actually 30/1.001 fps, which is 29.97002997970029… Conveniently for drop-frame timecode, we’ve got two zeroes after the 97 which means that the disparity between 29.97 and 30/1.001 is actually quite small. Over ten minutes, the error is about 0.018 frames. Over an hour, it’s about 0.107. Over an entire day, it’s about 2.59 frames. For editors, this is never something that you have to deal with or care about. Even a very long project can just be edited and delivered with drop-frame timecode and everything is fine. It’s really only broadcasters who somehow have to account for this over the course of a full day of programming. I frankly wasn’t sure how this is dealt with, so I asked on a broadcast engineering forum. One user, Jeff, an engineer in Syracuse, had the following reply:

Master sync / timecode generators like our Evertz 5601MSC use a GPS reference, so not only is the video reference spot on for frequency, the time code is extremely stable. Nevertheless, at any given moment there will be some amount of error between the (drop frame) time code versus real time. The solution to this is called “jam sync,” which essentially resets the time code, forcing it to match the real time once again. Some systems perform the jam when the error exceeds a set threshold; as you can imagine, this creates a fairly large jump and can be disruptive. Our equipment automatically performs the jam daily (ours takes place at 2am, when the automation doesn’t care). Not only does this correct for the relatively small mathematical imprecision you notice, it also makes provision for events like leap seconds that occur from time to time.

Naturally, this is only significant for operations that use time code to reflect real time — to control automation, or to index continuous recordings like air check systems. For time code within the context of specific program content, even shows that run several hours, uncorrected drop-frame code is accurate enough to run cleanly.

I’ve had to jam sync devices on a film set before, this is a way to be sure that all your equipment (cameras, audio, slate, etc.) have matching timecode. Since different internal timecode generators can drift slightly over time, you periodically re-sync everything to one master timecode generator. I didn’t know that similar equipment is used by broadcast engineers, but it makes sense. Another user, dcwar, pointed out that the manual for the Evertz Master Clock System that Jeff uses is available online. Page 54 has more detail about the jam sync process as well as a couple very cool graphs showing how drop-frame timecode can drift over the course of a day.

So there you have it, that’s why the very slight error in rounding frame rates to 29.97 doesn’t cause all television programming to slip by  a few frames each day.