PDA

View Full Version : DVD ripping using mencoder under Win32


AlienMind
30th November 2008, 17:39
If you want to have a RIP of all the titles of a DVD having all audio and subtitletracks like they existed, optimizing for stereo output, save the following as "rip.vbs" and read "Installation". It will generate "1.mkv", "2.mkv" etc. corresponding to your DVD's titles. Seriously, this is how it's done. It's even fire and forget. Hopefully we see fewer crap-rips on the tubes like this.

' Installation:
'
' 1) Download "MPlayer/MEncoder svn rev 27811" or higher @ http://oss.netfarm.it/mplayer-win32.php ,
' extract somewhere and set "mplayerPath" to it.
'
' 2) Download "MKVToolnix installer for v2.4.0" or higher @ http://www.bunkus.org/videotools/mkvtoolnix/downloads.html#windows ,
' install and set "mkvtoolnixPath" to it.
'
' 3) Create empty directory somewhere,
' save this script there
' do "cmd" and switch to that directory
'
'
' Usage:
' Adjust sizeMB below (size of whole DVD after storage)
' Enter: cscript rip.vbs
' This will store all dvd's title(s) (with all audio and subtitle tracks) to local directory
' using h264/aac (and multiple cpu's if you have them) correctly cropped and scaled as mkv file(s)
'
' Known Vista Annoyance:
' - I've detected a "mplayer has stopped working" in M$ Vista after the second pass encoding step.
' Please just push "quit program" here.
' It's the "problem report service" "werfault.exe" and there is NO way to deactivate this,
' you can only change it's looks in the control panel...
' BUT: Just write a batch file and put the following 4 lines in it:
' :a
' ping 127.0.0.1
' taskkill /IM werfault.exe
' goto a
' Run that in a new "cmd" and it will take care of that prick.
'
' Changelog:
' - removed brdo:bime for MPlayer-p4-svn-27811
'
' - added cooler stuff:
' adding partitions=all after direct_pred for enabling bigger block-sizes at all
' adding 8x8dct after partitions=all for intelligent macroblock choosing
' adding bframes=3 after frameref for enabling autoinsertion of 0-3 bframes at all
'
' - better stereo handling (using drc info out of vobs)
'
' - improved interlaced functionality (just double fps using filter yadif, see
' http://guru.multimedia.cx/deinterlacing-filters/)
'
' Comments:
' The subwindows are still getting focus (http://msdn.microsoft.com/en-us/library/d5fk67ky(VS.85).aspx)
' Already Tried 8, 10
'
' If you get "too much audio frames", you must omit "-noskip" and fix the audio later
'
' Afterwards, reorder/label audio-tracks like: "C:\Program Files\MKVtoolnix\mkvmerge" -o dkv2_.mkv --track-order 0:1,0:3,0:2 --language 2:ger --language 3:eng dkv2.mkv



'size of rip in MB
sizeMB= 750

mplayerPath= "C:\temp\MPlayer-p4-svn-29355"

mkvtoolnixPath= "C:\Program Files\MKVtoolnix"

'wherever "video_ts" is. for example, your DVD drive: "D:". Omit spaces
' and any special chars (e.g. brackets) in that path! OMIT FINAL \ too!
dvdPath= "Y:\directories\some.copied.dvd.content"

'if you have a interlaced video source (and thus, combs from hell in the target), set this to "True"
interlaced= False




' assume dot as comma
SetLocale("en-us")

function error(str)
WScript.StdOut.Write(str)
WScript.Quit(1)
end function

function print(str)
'WScript.StdOut.Write(str & '\r\n')
WScript.Echo(str)
end function


Set fsObj = CreateObject("Scripting.FileSystemObject")

dim sidArr()
' first element is always skipped because UBound function breaks on null entry otherwise
ReDim Preserve sidArr(1)
sidArr(1) = "dummy"

dim aidArr()
' first element is always skipped because UBound function breaks on null entry otherwise
ReDim Preserve aidArr(1)
aidArr(1) = "dummy"


if interlaced then
filterAddStr= "yadif=1,"
fpsFactor= 2
else
filterAddStr= ""
fpsFactor= 1
end if



' Get..
cmdStr= mplayerPath & "\mplayer.exe -identify -nosound -novideo -dvd-device " & dvdPath & " dvd://999"
Set shellObj = WScript.CreateObject("WScript.Shell")
print("---")
print("Total inventory Command: " & cmdStr)
Set execObj = shellObj.Exec(cmdStr)
str = execObj.StdOut.ReadAll
Set regxObj = New RegExp
regxObj.Global = True

' .. title count
regxObj.Pattern = "ID_DVD_TITLES=([0-9]+)"
Set matchObj= regxObj.Execute(str)
if matchObj.Count <> 1 then
error("ERR: parse title count")
end if
titleCount= matchObj.Item(0).Submatches(0)

' .. total length => totalLength
regxObj.Pattern = "ID_DVD_TITLE_[0-9]+_LENGTH=([0-9.]+)"
Set matchObj= regxObj.Execute(str)
totalLength= 0
for i = 1 to matchObj.Count
totalLength= totalLength + CDbl(matchObj.Item(i-1).Submatches(0))
next

' ..subtitle-ids (info can't be fetched off the .vob's later) => sidArr
regxObj.Pattern = "ID_SUBTITLE_ID=([0-9]+)"
Set matchObj= regxObj.Execute(str)
for sidNum = 2 to matchObj.Count + 1
ReDim Preserve sidArr(sidNum)
sidArr(sidNum)= CInt(matchObj.Item(sidNum-2).Submatches(0))
next

' .. assuming two audiotracks with 80kb/s, the movie bitrate to use
videoBitrate= ( ( (sizeMB*2^20)-(totalLength*((80+80)*1000/8)) ) / ( totalLength ) ) * 8 / 1000
' this is some magic value, don't ask
videoBitrate= CInt(videoBitrate * 0.92)
' this is a safety lock against shitty quality
if videoBitrate < 700 then
videoBitrate = 700
end if








for titleNum = 1 to titleCount

' Rip title
cmdStr= mplayerPath & "\mplayer.exe -benchmark -nosound -vo null -dumpstream -dumpfile " & titleNum & ".vob -dvd-device " & dvdPath & " dvd://" & titleNum
print("---")
print("Rip Command: " & cmdStr)
returnVal= shellObj.Run(cmdStr, 0, true)
next



for titleNum = 1 to titleCount
'for titleNum = 3 to 3

' Get..
cmdStr= mplayerPath & "\mplayer.exe -identify -benchmark -nosound -vo null -endpos 1 " & titleNum & ".vob"
print("---")
print("Track inventory Command: " & cmdStr)
Set execObj = shellObj.Exec(cmdStr)
str = execObj.StdOut.ReadAll

' ..audiotrack-ids => aidArr
regxObj.Pattern = "ID_AUDIO_ID=([0-9]+)"
Set matchObj= regxObj.Execute(str)
for aidNum = 2 to matchObj.Count +1
ReDim Preserve aidArr(aidNum)
aidArr(aidNum)= CInt(matchObj.Item(aidNum-2).Submatches(0))
next

' ..aspect ratio => aspectX I won't use ID_VIDEO_ASPECT because it appears
' twice, and the first time, it has 0.
regxObj.Pattern = "Movie-Aspect is ([0-9.]+):1"
Set matchObj= regxObj.Execute(str)
aspectX= CDbl(matchObj.Item(0).Submatches(0))

' ..source video width => videoW
regxObj.Pattern = "ID_VIDEO_WIDTH=([0-9]+)"
Set matchObj= regxObj.Execute(str)
videoW= CInt(matchObj.Item(0).Submatches(0))

' ..source video height => videoH
regxObj.Pattern = "ID_VIDEO_HEIGHT=([0-9]+)"
Set matchObj= regxObj.Execute(str)
videoH= CInt(matchObj.Item(0).Submatches(0))

' .. fps
regxObj.Pattern = "ID_VIDEO_FPS=([0-9.]+)"
Set matchObj= regxObj.Execute(str)
fps= CDbl(matchObj.Item(0).Submatches(0))



' Get crop detection info at the middle of the title..
' On some DVD's -ss doesen't work for seconds, but for filesize in mb:
Set objFile= fsObj.GetFile(titleNum & ".vob")
fSizeByte= objFile.Size
cmdStr= mplayerPath & "\mplayer.exe -v -ss " & (fSizeByte/1024/1024/2) & "mb -endpos 140 -benchmark -nosound -vf cropdetect=16:16 -vo null " & titleNum & ".vob"
print("---")
print("Cropdetection Command: " & cmdStr)
Set execObj = shellObj.Exec(cmdStr)
str = execObj.StdOut.ReadAll

' .. pixelX / pixelY / cropX / cropY
regxObj.Pattern = "crop=([0-9]+):([0-9]+):([0-9]+):([0-9]+)"
Set matchObj= regxObj.Execute(str)
if matchObj.Count = 0 then
pixelX = videoW
pixelY = videoH
cropX = 0
cropY = 0
else
pixelX= CInt(matchObj.Item(matchObj.Count-1).Submatches(0))
pixelY= CInt(matchObj.Item(matchObj.Count-1).Submatches(1))
cropX= CInt(matchObj.Item(matchObj.Count-1).Submatches(2))
cropY= CInt(matchObj.Item(matchObj.Count-1).Submatches(3))
end if




' Correct y height according to x/y ratio of aspectX:1
scaledY= CInt(pixelY * (videoW / (aspectX * videoH)))
scaledY= scaledY-(scaledY mod 16)

print("===")
print("Extracting " & titleNum & ".h264 . Taking rectangle of " & pixelX & "x" & pixelY & " at " & cropX & "x " & cropY & "y. According to aspect " & aspectX & " of source " & videoW & "x" & videoH & ", downscaling height to " & scaledY & " .")

' 1st pass
cmdStr= mplayerPath & "\mencoder -v -noskip " & titleNum & ".vob -nosound -vf " & filterAddStr & "crop=" & pixelX & ":" & pixelY & ":" & cropX & ":" & cropY & ",scale=" & pixelX & ":" & scaledY & " -ovc x264 -x264encopts frameref=6:bframes=3:b_pyramid:direct_pred=auto:partitions=all:8x8dct:me=umh:subq=7:me_range=64:trellis=2:nofast_pskip:bitrate=" & videoBitrate & ":pass=1:turbo=1:threads=auto -of rawvideo -o NUL"
print("---")
print("First Pass Command: " & cmdStr)
returnVal= shellObj.Run(cmdStr, 0, true)


' 2nd pass
cmdStr= mplayerPath & "\mencoder -v -noskip " & titleNum & ".vob -nosound -vf " & filterAddStr & "crop=" & pixelX & ":" & pixelY & ":" & cropX & ":" & cropY & ",spp,scale=" & pixelX & ":" & scaledY & ",hqdn3d=2:1:2 -ovc x264 -x264encopts frameref=6:bframes=3:b_pyramid:direct_pred=auto:partitions=all:8x8dct:me=umh:subq=7:me_range=64:trellis=2:nofast_pskip:bitrate=" & videoBitrate & ":pass=2:threads=auto -of rawvideo -o " & titleNum & ".h264"
print("---")
print("Second Pass Command: " & cmdStr)
returnVal= shellObj.Run(cmdStr, 0, true)

' Audiotracks
for i = 2 to UBound(aidArr)
print("Extracting audio id " & aidArr(i) & " as " & titleNum & "-" & (i-1) & ".aac")
'cmdStr= mplayerPath & "\mencoder -v -noskip " & titleNum & ".vob -ovc copy -aid " & aidArr(i) & " -af resample=44100:0:2,channels=2,volnorm -oac faac -faacopts tns:br=80 -of rawaudio -o " & titleNum & "-" & (i-1) & ".aac"
cmdStr= mplayerPath & "\mencoder -v -noskip " & titleNum & ".vob -ovc copy -aid " & aidArr(i) & " -a52drc 1 -channels 2 -srate 44100 -af-adv force=1 -oac faac -faacopts tns:br=80 -of rawaudio -o " & titleNum & "-" & (i-1) & ".aac"
print("---")
print("Audio Command: " & cmdStr)
returnVal= shellObj.Run(cmdStr, 0, true)
next

' Subtitles
for i = 2 to UBound(sidArr)
print("Extracting subtitle id " & sidArr(i) & " as " & titleNum & "-" & (i-1) & ".idx/sub")
' the .ifo contains color palette for subtitle images
cmdStr= mplayerPath & "\mencoder -v -noskip " & titleNum & ".vob -ovc copy -oac copy -sid " & sidArr(i) & " -ifo " & dvdPath & "\video_ts\vts_01_0.ifo -vobsubout " & titleNum & "-" & (i-1) & " -o NUL"
print("---")
print("Subtitle Command: " & cmdStr)
returnVal= shellObj.Run(cmdStr, 0, true)
next

' Join
cmdStr= """" & mkvtoolnixPath & "\mkvmerge.exe"" -o " & titleNum & ".mkv --default-duration 0:" & (fps*fpsFactor) & "fps --aspect-ratio 0:" & pixelX & "/" & scaledY & " " & titleNum & ".h264"
for i = 2 to UBound(aidArr)
cmdStr= cmdStr & " " & titleNum & "-" & (i-1) & ".aac"
next
for i = 2 to UBound(sidArr)
cmdStr= cmdStr & " " & titleNum & "-" & (i-1) & ".idx"
next
print("---")
print("Join Command: " & cmdStr)
returnVal= shellObj.Run(cmdStr, 0, true)
next


Please give me a hand if you find issues with that script, as I only have tested it on like 10 DVD's so far.

Changed version: Interlaced Option and took out volume normalization because it's always bad for the experience, info in DVD (-a52drc 1) must suffice to achieve good stereo setting.
Another change: Improved interlaced functionality (just double fps using filter yadif)

As I've discovered my first "state-of-the-art" DVD (defect sectors etc) this week, I advise you to always use the AnyDVD CD Emulator, it takes care for that.

dat720
30th November 2008, 19:53
Dude beautiful work there..... nice to see some VBS solutions.

Couple of suggestions, why not make it force itself to run in cscript? (i can provide a sample for this, its only 4 lines)
and
Why not use more code to detect which optical drives are available then search the drive for a video-ts folder? (i could also help with this)

Haven't tried it yet, about to leave for work (it's just before 6am here!!!) i will definetly give it ago this afternoon when i get home.

dat720
1st December 2008, 07:28
If LCase(Right(Wscript.FullName, 11)) = "wscript.exe" Then
Set objShell = CreateObject("Wscript.Shell")
objShell.Run("%comspec% /c cscript.exe /nologo " & """" & Wscript.ScriptFullName & """") : Wscript.Quit
End If

Checks to see if the scripting host is wscript, if it is then it re runs the script via cscript and kills the wscript session.

AlienMind
1st December 2008, 19:09
Thanks, good stuff.

Btw, you can replace every
shellObj.Run(cmdStr, 0, true)
with e.g.
shellObj.Run(cmdStr, 8, true)
to open a subwindow to see the output of each fired command (but if there was an error, it just disappears, so my solution atm is to keep it at 0 and just copy&paste the doubful printed command to execute it anew to really see what's going on).
Also, I can't prevent that subwindow from getting focus.

The second thing I still don't like ATM is the time for the command after "' Rip title". It's lightning fast if the source is on some HD or even network drive, but if it's a real DVD, it's taking as long as playing the real DVD it seems.

I already apologize for maybe not wanting to improve that script to "production use", you know, with GUI and stuff, for me it'll always stay a script.

dat720
1st December 2008, 19:49
you could always open the window normally and then use objShell.AppActivate to bring the main window back into focus

and scripts are good.... they can always be messed with and tinkered with without needing recompiling!

PS work on the script till you are happy with it then maybe use somehting like vb.net 2008 express to build a program out of it.