The basic approach is to add black borders to your video to make it fit in one of DVD's allowed aspect ratios.
TLDR: skip down to In Conclusion.
A few definitions
First, though, I need to define a couple of different things:
- An aspect ratio is simply the width of something divided by the height, typically expressed as a fraction. Often the traditional slash is replaced with a colon: we write 4:3 instead of 4⁄3. Sometimes, these are expressed in decimal (1.333…). You could also call it 8:6, 12:9, 16:12, etc, as those are all equal. Or even 1.333:1 (equal, if only you could write enough 3s).
- A display aspect ratio (DAR) is the aspect ratio of an actual display (e.g., a TV). Common displays are nominally 4:3 or 16:9.
- A storage aspect ratio (SAR) is the ratio of width to height (in pixels) of the stored image or video. For example, NTSC DVD Video maxes out at 720 by 480 ("Full D1"), which has a SAR of 1.5:1.
- A pixel aspect ratio is the aspect ratio of a single pixel in a stored image. In video, pixels are not always square. When non-square, they are usually narrower than they are tall.
There is a simple mathematical relationship between the three above: SAR × PAR = DAR. As an example, 720:480 * 8:9 = 4:3. That'd be video for a 4:3 display put on a DVD at full resolution.
Analog television does not have pixels. Instead, it has a continuously varying signal. A certain portion of that signal is supposed to be projected onto each line of the display, then there is an inactive time to allow for, e.g., moving to the next line. Different TVs would display slightly different amounts of each line, though, and that could vary over the lifetime of the TV, or even as the TV warmed up.
DVD says not to use the leftmost and rightmost 8 pixels. So out of 720, 704 are supposed to be used. The 8 pixels on each side are supposed to be filled with black. The specified PAR is this 10:11.
Of course, people used to digital equipment find this silly (to use words suitable for polite company). And many commercial DVD releases actually use all 720 pixels, some expecting a 10:11 PAR and some expecting 8:9. [Or similar for a 16:9 DAR]. Most hardware DVD players use 10:11, though of course it'll also depend on the TV settings.
If you're starting with video where you want every pixel displayed, you probably want to fit it in to 704x480 (SAR 22:15), with the 8px black bars. If the sides being cut off is OK, then you can use the full 720x480. Either way, you need to scale your video as required to get a PAR of either 10:11 (DAR of 4:3) or 40:33 (DAR of 16:9) and, if less than the full 720x480 frame, add black bars.
Actually doing it
ffmpeg can do this. On the unofficial ffmpeg support forum, ks_kalvan offers an ffmpeg video filter chain for targeting a 16:9 DAR:
How does that work‽
Note there are three filters in there:
setsar. We'll take each in turn, from the end, as the end ones are the simplest.
The last filter is confusing until you check the documentation (`man ffmpeg-filter) and find this expletive removed line: "the "setsar" filter sets the Sample (aka Pixel) Aspect Ratio for the filter output video." So that is actually setting the PAR to 40:33, which is the value we said we wanted to use, above.
pad filter adds black borders. The documentation tells us that ow is the output width (i.e., 720, from the
w=720 part), oh the output height (i.e., 480, from the
h=480 part). iw and ih are the input width and height, respectively. ow-iw is thus the number of pixels we're adding to the width (and similarly oh-ih for the height); dividing by 2 puts half of that on each side of the picture. In other words, we're centering the picture.
scale filter resizes the video. The
w= option specifies the new/output width and
h= option specifies the height. Again, it's an expression, but more complicated. The width and the height formulas are the same, just with the height and width swapped. Let's examine the width (
Functions and operators are documented in
man ffmpeg-util. For positive numbers,
trunc(x+0.5) is trick to get round-to-nearest-integer, something which ffmpeg doesn't have.
Building on that
trunc(x+0.5) trick, we have
trunc(x/2+0.5)*2. x/2 gives us half x, of course; the truncate then rounds it to the nearest integer. Doubling it then gives us the nearest even number.
I'm going to use «W» where the actual command like uses 720. That's the final output width in pixels. Similarly, «H» instead of 480 (final output height). Instead of 40/33, «PAR», as that's the target pixel aspect ratio. And PAR⁻¹ is the reciprocal of PAR, i.e., 33/40.
dar is a ffmpeg variable. It is the DAR of the input video.
A key thing to understand this is the calculation in the middle, where we have a calculation that is «H» × dar ÷ «PAR». Remember that dar is the display w/h of the original video. So multiplying the target width (in pixels) by the dar gives us how wide it'd be if we scaled the video to have the target height «H» and square pixels as well. Dividing by the PAR then converts that into width in the non-square pixels we're actually using.
min(«W», # take the minimum [lesser of] target width and...
trunc( # the truncation of (round to integer towards 0)
(«H»*«PAR⁻¹»*dar) # calculate the wanted output width, see note above
/ 2 + 0.5) * 2 # and get the nearest even number to that
Example: Say you take a 1280 by 720 pixel video with a 16:9 display aspect ratio. Starting from the middle:
- «H»*«PAR⁻¹»*dar = 480*33/40*16/9 = 704.
- 704 / 2 = 352.
- trunc(352 + 0.5) = 352
- 352 * 2 = 704
- min(720, 704) = 704
Which is the full usable width (that is, excluding the 8px unusable areas), as we'd expect of a 16:9 video on a 16:9 display.
And when we do it on height, we get 490, which thanks to the min() is held to 480. But actually, that shows that we probably wanted to use 704 there (the usable width) instead of 720, which comes to precisely 480. So it'd appear that there is a minor bug there, causing a tiny (and probably imperceptible) distortion. That's fixed below, I believe.
ffmpeg -i YOUR-FILE-HERE \
-filter:v "scale='w=min(720,trunc((480*33/40*dar)/2+0.5)*2):h=min(480,trunc((704*40/33/dar)/2+0.5)*2)',pad='w=720:h=480:x=(ow-iw)/2:y=(oh-ih)/2',setsar='r=40/33'" \
-target ntsc-dvd YOUR-OUTPUT-HERE.mpg
NOTE: Older ffmpeg's don't have
-filter:v. You can try
-vf instead (untested), or better yet download a new static build from ffmpeg.org.