Burning a video file with non-standard aspect ratio to DVD

burningdvdffmpeg

A standard Unixy command line approach to burning a video file to DVD is as follows (assuming)

# 1. First use ffmpeg to convert to an mpg file.

ffmpeg -i input.m4v -target ntsc-dvd output.mpg

# 2. now do the authoring

dvdauthor --title -o dvd -f output.mpg
dvdauthor -o dvd -T

NOTE: --title sets the title of the DVD, -T sets the table of contents. In both above commands the -o switch is referencing a directory, NOT the actual dvd.

# 3. roll the .mpg file into an ISO file
genisoimage -dvd-video -o dvdimage.iso dvd

Finally, burn the resulting iso file to a DVD blank. I use Brasero, which is reliable.

However, this method does not work well with non-standard aspect ratios. The DVD format is quite rigid about specifying what aspect ratios are accepted. You'll know if you have this problem if dvdauthor says something similar to

WARN: unknown mpeg2 aspect ratio 1

What is a good way to modify this method to handle non-standard aspect ratios?

UPDATE: Thanks to Anthony fo the very thorough and clear answer.
I hope this will be useful for people trying to find an answer for this
vexing issue. I don't know of any other clear explanation of
this on the net.

Best Answer

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 43. 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.

Another complexity

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.

Summary

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

Thankfully, ffmpeg can do this. On the unofficial ffmpeg support forum, ks_kalvan offers an ffmpeg video filter chain for targeting a 16:9 DAR:

-filter:v "scale='w=min(720,trunc((480*33/40*dar)/2+0.5)*2):h=min(480,trunc((720*40/33/dar)/2+0.5)*2)',pad='w=720:h=480:x=(ow-iw)/2:y=(oh-ih)/2',setsar='r=40/33'"

How does that work‽

Note there are three filters in there: scale, pad, and 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.

The 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.

The 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 (w=) formula:

  • 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:

  1. «H»*«PAR⁻¹»*dar = 480*33/40*16/9 = 704.
  2. 704 / 2 = 352.
  3. trunc(352 + 0.5) = 352
  4. 352 * 2 = 704
  5. 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.

In conclusion

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.

Related Question