encryptio.com

This sentence is very meta.

Over-The-Shoulder Manual DVDRiP

I decided to make an OTS example of my DVD ripping, as most geeks that have seen my rips wonder how I did it—now I dont have to repeat myself over and over.

Tools

Steps

Set Up Workspace

ckastorff@wide:~ $ mkdir monty-python
ckastorff@wide:~ $ cd monty-python/
ckastorff@wide:~/monty-python $

Ripped DVD to Hard Drive

First, I found the device node for my DVD—this part will change depending on the OS. In OS Xs case, I went to Disk Utility and found the device name by viewing extra info on the Volume. The device name was /dev/disk1. I then unmounted the DVD so that dvdbackup wouldnt have any issues.

ckastorff@wide:~/monty-python $ dvdbackup -i /dev/disk1 -F -o dvdrip
libdvdread: Using libdvdcss version 1.2.9 for DVD access

libdvdread: Attempting to retrieve all CSS keys
libdvdread: This can take a _long_ time, please be patient

libdvdread: Get key for /VIDEO_TS/VIDEO_TS.VOB at 0x000001c0
libdvdread: Elapsed time 0
libdvdread: Get key for /VIDEO_TS/VTS_01_0.VOB at 0x000022c0
libdvdread: Elapsed time 0
libdvdread: Get key for /VIDEO_TS/VTS_01_1.VOB at 0x0002fe3a
libdvdread: Elapsed time 0
libdvdread: Get key for /VIDEO_TS/VTS_02_0.VOB at 0x0028ab40
libdvdread: Elapsed time 0
libdvdread: Get key for /VIDEO_TS/VTS_02_1.VOB at 0x0028ab60
libdvdread: Elapsed time 0
libdvdread: Found 2 VTS's
libdvdread: Elapsed time 0

While waiting on that, I talked on IRC.

<encryptio> jordgubbe: sup
<jordgubbe> still hunting for stuff to delete.
<jordgubbe> :)
<encryptio> coo
<jordgubbe> you?
<jordgubbe> :P
<encryptio> copying things from irc to put in the dvd ripping over-the-shoulder tutorial
<encryptio> you'll be famous!

Strip and Join

I checked, with VLC, which VOBs are the actual movie—turns out only VTS_01_0.VOB was not. I removed it and the IFO and BUP files, as I wont be needing them.

ckastorff@wide:~/monty-python/dvdrip/VIDEO_TS $ rm *.BUP *.IFO VTS_01_0.VOB
ckastorff@wide:~/monty-python/dvdrip/VIDEO_TS $ ls
VTS_01_1.VOB VTS_01_2.VOB VTS_01_3.VOB VTS_01_4.VOB VTS_01_5.VOB

I then joined the VOBs into a single VOB so that ffmpeg would have an easier time dealing with the file, then killed the split files.

ckastorff@wide:~/monty-python/dvdrip/VIDEO_TS $ cat VTS_01_{1,2,3,4,5}.VOB > joined.vob
ckastorff@wide:~/monty-python/dvdrip/VIDEO_TS $ rm VTS_*

Transcoded the Audio Track

First, I needed to know which track I wanted to get. I got info on the file with ffmpeg...

ckastorff@wide:~/monty-python $ ffmpeg -i joined.vob                       
FFmpeg version CVS, Copyright (c) 2000-2004 Fabrice Bellard
Mac OSX universal build for ffmpegX
  configuration:  --enable-memalign-hack --enable-mp3lame --enable-gpl --disable-vhook
  --disable-ffplay --disable-ffserver --enable-a52 --enable-xvid --enable-faac --enable-faad
  --enable-amr_nb --enable-amr_wb --enable-pthreads --enable-x264
  libavutil version: 49.0.0
  libavcodec version: 51.9.0
  libavformat version: 50.4.0
  built on Apr 15 2006 04:58:19, gcc: 4.0.1 (Apple Computer, Inc. build 5250)
Input #0, mpeg, from 'joined.vob':
  Duration: 00:31:33.9, start: 0.041500, bitrate: 21358 kb/s
  Stream #0.0[0x1e0], 29.97 fps(r): Video: mpeg2video, yuv420p, 720x480, 9800 kb/s
  Stream #0.1[0x84]: Audio: ac3, 48000 Hz, stereo, 192 kb/s
  Stream #0.2[0x83]: Audio: ac3, 48000 Hz, stereo, 192 kb/s
  Stream #0.3[0x82]: Audio: ac3, 48000 Hz, stereo, 192 kb/s
  Stream #0.4[0x81]: Audio: ac3, 48000 Hz, 5:1, 448 kb/s
  Stream #0.5[0x80]: Audio: ac3, 48000 Hz, stereo, 192 kb/s
  Stream #0.6[0x26]: Subtitle: dvdsub
  Stream #0.7[0x25]: Subtitle: dvdsub
  Stream #0.8[0x24]: Subtitle: dvdsub
  Stream #0.9[0x23]: Subtitle: dvdsub
  Stream #0.10[0x22]: Subtitle: dvdsub
  Stream #0.11[0x21]: Subtitle: dvdsub
Must supply at least one output file

Now I knew the audio tracks were on tracks #0.1, #0.2, #0.3, #0.4, and #0.5. After a little trial and error, I found that that 5.1 track (#0.4) was the correct track—the others were commentaries by various people, and #0.5 was a french track. I decoded that track to a wav file.

ckastorff@wide:~/monty-python $ ffmpeg -i joined.vob -vn -map 0.4:0.0 out.wav
<<snip>
Input #0, mpeg, from 'joined.vob':
<<snip>>
Output #0, wav, to 'out.wav':
  Stream #0.0: Audio: pcm_s16le, 48000 Hz, stereo, 1536 kb/s
Stream mapping:
  Stream #0.4 -> #0.0 [sync #0.0]
Press [q] to stop encoding
size= 1036614kB time=5528.6 bitrate=1536.0kbits/s    
video:0kB audio:1036614kB global headers:0kB muxing overhead 0.000004%

Then I encoded it into the AAC track I needed, with a slightly higher bitrate than it would normally output.

ckastorff@wide:~/monty-python $ faac -q 133 out.wav
Freeware Advanced Audio Coder
FAAC 1.24+ (Sep  7 2007) UNSTABLE

Quantization quality: 133
Bandwidth: 19960 Hz
Object type: Low Complexity(MPEG-2) + M/S
Container format: Transport Stream (ADTS)
Encoding out.wav to out.aac
   frame            | bitrate | elapsed/estim | play/CPU | ETA
259155/259155 (100%)|  141.6  |  269.1/269.1  |   20.55x | 0.0

Transcoded the Video Track

First, I needed to know what bitrate I was supposed to use—I knew my target size was 690MiB, and the audio was already taking up 93.3MiB. I went to my bot for advice:

<encryptio> .conv (690mb-93.3mb)/90m>kbps
<boobs> encryptio: 905.216 kbps

There were small black borders around the image in the DVD, something I wanted to remove—so I used ffmpegs -crop* options to remove them, finding the values via trial and error. I then encoded the first pass.

ckastorff@wide:~/monty-python $ ffmpeg -i joined.vob -f yuv4mpegpipe -croptop 6 -cropleft 6 \
-cropbottom 8 -s 720x400 -r 30000/1001 - | x264 -I 100 -b 5 --b-pyramid --ref 5 -A all --weightb \
--me umh --merange 24 --subme 7 --b-rdo --mixed-refs --bime --8x8dct --trellis 2 --qcomp 0.9 \
--bitrate 905 --threads 2 --fps 30000/1001 --pass 1 -o video.mp4 - 720x400

<<snip>>
Output #0, yuv4mpegpipe, to 'pipe:':
  Stream #0.0, 29.97 fps(c): Video: rawvideo, yuv420p, 720x400, q=2-31, 200 kb/s
Stream mapping:
  Stream #0.0 -> #0.0
Press [q] to stop encoding
frame=165699 q=0.0 Lsize=69904266kB time=5528.8 bitrate=103576.4kbits/s
video:0kB audio:0kB global headers:0kB muxing overhead inf%
x264 [info]: slice I:2413  Avg QP:21.44  size: 24789  PSNR Mean Y:44.91 U:49.18 V:49.71 Avg:45.76 Global:43.84
x264 [info]: slice P:86723 Avg QP:23.70  size:  5043  PSNR Mean Y:42.30 U:46.64 V:47.34 Avg:43.26 Global:41.95
x264 [info]: slice B:76563 Avg QP:24.51  size:  1512  PSNR Mean Y:45.05 U:51.49 V:52.11 Avg:46.06 Global:41.80
x264 [info]: mb I  I16..4: 18.7% 68.5% 12.9%
x264 [info]: mb P  I16..4:  2.9%  6.6%  0.6%  P16..4: 45.4% 11.0%  4.0%  0.2%  0.1%    skip:29.3%
x264 [info]: mb B  I16..4:  0.3%  0.7%  0.1%  B16..8: 28.4%  0.9%  1.3%  direct: 2.0%  skip:66.5%
x264 [info]: final ratefactor: 21.91
x264 [info]: 8x8 transform  intra:65.8%  inter:72.1%
x264 [info]: ref P  71.5% 11.9%  8.7%  4.0%  3.9%
x264 [info]: ref B  72.2% 12.8%  8.3%  3.6%  3.0%
x264 [info]: PSNR Mean Y:43.605 U:48.922 V:49.579 Avg:44.589 Global:41.902 kb/s:886.83

encoded 165699 frames, 7.02 fps, 888.17 kb/s

While waiting on that, I noticed I had made a mistake. The movie wasnt 90 minutes long, it was slightly longer: 92 minutes. So I took the new length into account, calculated my new bitrate, and continued with the second pass.

<encryptio> .conv (690mb-93.3mb)/5528.8s>kbps
<boobs> encryptio: 884.127912024313 kbps
ckastorff@wide:~/monty-python $ ffmpeg -i joined.vob -f yuv4mpegpipe -croptop 6 -cropleft 6 \
-cropbottom 8 -s 720x400 -r 30000/1001 - | x264 -I 100 -b 5 --b-pyramid --ref 5 -A all --weightb \
--me umh --merange 24 --subme 7 --b-rdo --mixed-refs --bime --8x8dct --trellis 2 --qcomp 0.9 \
--bitrate 884 --threads 2 --fps 30000/1001 --pass 3 -o video.mp4 - 720x400

<<snip>>
Press [q] to stop encoding
frame=165699 q=0.0 Lsize=69904266kB time=5528.8 bitrate=103576.4kbits/s
video:0kB audio:0kB global headers:0kB muxing overhead inf%
x264 [info]: slice I:2413  Avg QP:21.37  size: 25698  PSNR Mean Y:45.04 U:49.30 V:49.83 Avg:45.88 Global:44.09
x264 [info]: slice P:86723 Avg QP:23.63  size:  5031  PSNR Mean Y:42.43 U:46.75 V:47.43 Avg:43.38 Global:42.24
x264 [info]: slice B:76563 Avg QP:24.67  size:  1473  PSNR Mean Y:45.01 U:51.62 V:52.22 Avg:46.02 Global:42.09
x264 [info]: mb I  I16..4: 18.4% 68.7% 12.9%
x264 [info]: mb P  I16..4:  2.7%  6.9%  0.7%  P16..4: 37.9% 11.1%  4.1%  0.2%  0.1%    skip:36.3%
x264 [info]: mb B  I16..4:  0.3%  0.7%  0.1%  B16..8: 28.8%  0.9%  1.3%  direct: 1.9%  skip:66.0%
x264 [info]: 8x8 transform  intra:67.4%  inter:75.2%
x264 [info]: ref P  68.0% 12.3% 10.3%  5.0%  4.5%
x264 [info]: ref B  74.2% 10.6%  8.5%  3.8%  3.0%
x264 [info]: PSNR Mean Y:43.664 U:49.037 V:49.676 Avg:44.640 Global:42.188 kb/s:884.13

encoded 165699 frames, 7.92 fps, 885.46 kb/s

And the third and final pass...

ckastorff@wide:~/monty-python $ ffmpeg -i joined.vob -f yuv4mpegpipe -croptop 6 -cropleft 6 \
-cropbottom 8 -s 720x400 -r 30000/1001 - | x264 -I 100 -b 5 --b-pyramid --ref 5 -A all --weightb \
--me umh --merange 24 --subme 7 --b-rdo --mixed-refs --bime --8x8dct --trellis 2 --qcomp 0.9 \
--bitrate 884 --threads 2 --fps 30000/1001 --pass 2 -o video.mp4 - 720x400

<<snip>>
Press [q] to stop encoding
frame=165699 q=0.0 Lsize=69904266kB time=5528.8 bitrate=103576.4kbits/s
video:0kB audio:0kB global headers:0kB muxing overhead inf%
x264 [info]: slice I:2413  Avg QP:21.31  size: 25755  PSNR Mean Y:45.05 U:49.32 V:49.85 Avg:45.89 Global:44.15
x264 [info]: slice P:86723 Avg QP:23.60  size:  5031  PSNR Mean Y:42.44 U:46.75 V:47.44 Avg:43.39 Global:42.27
x264 [info]: slice B:76563 Avg QP:24.55  size:  1470  PSNR Mean Y:45.02 U:51.63 V:52.23 Avg:46.03 Global:42.12
x264 [info]: mb I  I16..4: 18.1% 69.1% 12.8%
x264 [info]: mb P  I16..4:  2.7%  6.9%  0.7%  P16..4: 38.0% 11.1%  4.2%  0.2%  0.1%    skip:36.1%
x264 [info]: mb B  I16..4:  0.3%  0.7%  0.1%  B16..8: 28.8%  0.9%  1.4%  direct: 1.9%  skip:65.9%
x264 [info]: 8x8 transform  intra:67.3%  inter:75.2%
x264 [info]: ref P  68.0% 12.4% 10.2%  4.9%  4.4%
x264 [info]: ref B  73.9% 10.7%  8.5%  3.8%  3.0%
x264 [info]: PSNR Mean Y:43.670 U:49.041 V:49.690 Avg:44.645 Global:42.221 kb/s:884.09

encoded 165699 frames, 7.05 fps, 885.42 kb/s

Mux Video and Audio Tracks for Final File

Here, there was an odd error—the original VOBs indicated that the frame rate was 29.97 fps, but the actual frame rate was 29.971 fps. This caused a small delay on the order of a tenth of a second by the end of the movie. I had to demux the video track from its mp4 container and force its frame rate to the improper, but working frame rate.

ckastorff@wide:~/monty-python $ mp4box -raw 1 video.mp4
Extracting MPEG-4 AVC-H264 stream to h264

And heres the mux...

ckastorff@wide:~/monty-python $ mp4box -add video_track1.h264:fps=29.971 -add out.aac muxed.mp4
AVC-H264 import - frame size 720 x 400 at 29.971 FPS
Adjusting AVC SizeLength to 16 bits
Import results: 165699 samples - Slices: 4826 I 173446 P 153126 B - 1 SEI - 4270 IDR
        Stream uses B-slice references - max frame delay 2
AAC import - sample rate 48000 - MPEG-2 audio - 2 channels
Saving to muxed.mp4: 0.500 secs Interleaving

Make a Torrent for Other People That Own the Movie to Travel With

I would never advocate piracy!

ckastorff@wide:~/monty-python $ mv muxed.mp4 monty-python-and-the-holy-grail.mp4
ckastorff@wide:~/monty-python $ btmkt -private http://ann/url monty*