Saturday 11 April 2020

Using HandBrake to encode video DVDs

Now that I have a lot of spare time, I decided to re-encode some of the video DVDs I ripped years ago. I have tried various programs on Linux, such as dvd::rip, dvdbackup and HandBrake. I found some results poor, I could see TV scan lines in the result, or motion blur. This time on the recommendation of some reviews I decided to try HandBrake.

This turned out to be an excellent choice because HandBrake has come far over the years. It encodes to MP4 a.k.a M4V. My older encodes were a mix of AVI and MP4. HandBrake is very thorough. Even though I chose the Very Fast 720p30 profile the quality was great. (I didn't want to spend a lot of CPU time on higher quality as the source doesn't have enough quality anyway.) I also liked that there is a CLI version called HandBrakeCLI which I ran in scripts to extract separate chapters from titles.

Assuming you have a multi-core CPU, you may wish to use the taskset command to limit the number of cores used. When I set no limit on HandBrake, it used all the cores and I could see in the system log that the CPUs got throttled when they got too warm. There was no noticeable effect on interactive response, I could still surf the Internet and do other things. Another effect was fast encoding caused a read rate from the DVD drive so high that an external one on USB would occasionally reset and break the encoding. If scripted, I can just run it on the side and come back when it's finished. Typically my jobs ran at about 3x real time. So a 2 hour movie would be done in about 40 minutes.

Speaking of reading from the drive, it's best if you can copy the DVD to an ISO image on disk first to reduce wear. And if there are bad blocks in the DVD you might want to use ddrescue to recover as much as possible first. A tip, even though the DVD might have CSS, you want to do an image copy so that HandBrake can do the normal CSS decrypting.

Do try to find a ready made package for HandBrake. At first HandBrakeCLI didn't work for me, the loader said that it couldn't find symbols from the libdvdcss library. I decided to build from source. This took a couple of hours even on my multi-core CPU. And the problem didn't go away. Then I noticed that I had very old versions of HandBrakeCLI in my /usr/local/bin directory. There was absolutely nothing wrong with the latest package. I had been running a much older version. Duh!

Sunday 5 April 2020

bash ranges

This is not widely known, although it is adequately documented in the bash(1) man page thus:

A sequence expression takes the form {x..y[..incr]}, where x and y are either integers or single characters, and incr, an optional increment, is an integer. When integers are supplied, the expression expands to each number between x and y, inclusive. Supplied integers may be prefixed with 0 to force each term to have the same width. When either x or y begins with a zero, the shell attempts to force all generated terms to contain the same number of digits, zero-padding where necessary. When characters are supplied, the expression expands to each character lexicographically between x and y, inclusive, using the default C locale. Note that both x and y must be of the same type. When the increment is supplied, it is used as the difference between each term. The default increment is 1 or -1 as appropriate.

This means that you can expect these expressions to work:

$ echo {0..10}
0 1 2 3 4 5 6 7 8 9 10
$ echo {z..a}
z y x w v u t s r q p o n m l k j i h g f e d c b a
$ echo {01..20..2}

01 03 05 07 09 11 13 15 17 19
$ echo f{20..01..2}

f20 f18 f16 f14 f12 f10 f08 f06 f04 f02

In the last case, notice a prefix was prepended as you would expect. Suffixes work too, of course. -2 will also work. The direction is determined by the range.

Thus {01..20} is a more efficient alternative to using

$(seq -w 01 20)

Brace expansion happens early so no use storing the expression in a variable or having one of the start, end, or increment a variable.

In case you are still wondering what ranges are useful for, a typical use is in a for loop:
for month in {01..12}
do
...
done