Log in

View Full Version : Batch encoding to h264


grv575
11th January 2010, 05:30
Powershell script to batch 2-pass encode files to h264/aac using the same bitrate as the source file.

Required tools -
x264: http://x264.nl/
bepipe: http://speeddemosarchive.com/w/images/e/e3/BePipe.zip
neroaacenc: http://speeddemosarchive.com/w/images/6/62/NeroDigitalAudio.zip
mp4box: http://www.videohelp.com/tools/mp4box
AviSynth: http://sourceforge.net/projects/avisynth2/files/
avsutil: http://members.optusnet.com.au/squid_80/avsutil.zip

# An AVS script is generated for every AVI that is found in the source folder
# The source video total bitrate is approximated based on the filesize and video runtime. This will give a good value to use as the bitrate for a 2-pass encode to ensure
# little loss of quality during transcoding.
#
# If you haven't used Powershell before, you can download it from Microsoft for free, then type in "Set-ExecutionPolicy RemoteSigned" to allow execution of unsigned scripts.
# Save the code in a .ps1 file, navigate to that folder in Powershell (works just like in cmd.exe) and type in ".\encode.ps1".
# Or you can just open a cmd shell and type: "powershell .\encode.ps1"

# Description: Batch encoding from various file formats to x264/AAC in an MP4 container.
# Example Usage: .\encode.ps1
# Required tools: x264, bepipe, neroaacenc, mp4box, AviSynth, avsutil - be sure to adapt exe paths below

#Param (
#[string]$Path
#)

# Configure paths here.
# Videos to encode are in $Path
# Temporary files (AVS, video, audio) are saved in $TempPath which is cleared out after every file except on exceptions.
# Muxed video is saved to $OutPath
# $FileTypes lists all file types in $Path that will be encoded

$Path = 'C:\video\source\'
$TempPath = 'C:\video\temp\'
$OutPath = 'C:\video\encoded\'
$FileTypes = @("*.avi","*.mpg","*.mpeg","*.wmv","*.rmvb","*.mkv")

# Configure exe paths here.

$ExePath = @{}
$ExePath['x264'] = 'C:\video\bin\x264.exe'
$ExePath['bepipe'] = 'C:\video\bin\BePipe.exe'
$ExePath['neroaacenc'] = 'C:\video\bin\neroAacEnc.exe'
$ExePath['mp4box'] = 'C:\video\bin\MP4Box.exe'
$ExePath['avsutil'] = 'C:\video\bin\avsutil.exe'

# Configure AVS script and command line parameters here

$Config = @{}
$Config['AVS'] = 'DirectShowSource("$FilenameIn", convertfps=true)'
#$Config['x264'] = '--crf 22 --ref 5 --mixed-refs --bframes 3 --b-adapt 2 --b-pyramid --weightb --deblock -1:-1 --trellis 2 --partitions all --8x8dct --me umh ' +
#'--threads auto --thread-input --progress --no-psnr --no-ssim --output "$FilenameVideo" "$FilenameAVS"'
$Config['x264pass1'] = '--pass 1 --bitrate $BitrateKbps --stats "$FilenameStats" --output NUL "$FilenameAVS"'
$Config['x264pass2'] = '--pass 2 --bitrate $BitrateKbps --stats "$FilenameStats" --output "$FilenameVideo" "$FilenameAVS"'
$Config['neroaacenc'] = '-q 0.55 -if - -of "$FilenameAudio"'
$Config['AVSBitrate'] = 'DirectShowSource("$FilenameIn")' + "`r`n" +
'filename = "$FilenameDuration"' + "`r`n" +
'WriteFileStart(filename, "FrameCount/FrameRate")'

function Encode($FilenameIn) {
Trap {
write-error $_.Exception.Message
return
}

Write-Host "Encoding $FilenameIn"

$Basename = [system.io.path]::GetFilenameWithoutExtension($FilenameIn)
$FilenameAVS = $TempPath + $Basename + '.avs'
$FilenameAVSBitrate = $TempPath + $Basename + '_bitrate.avs'
$FilenameVideo = $TempPath + $Basename + '_video.mp4'
$FilenameAudio = $TempPath + $Basename + '_audio.mp4'
$FilenameStats = $TempPath + $Basename + '.stats'
$FilenameDuration = $TempPath + $Basename + '_duration.txt'
$FilenameOut = $OutPath + $Basename + '.mp4'

Write-Host '- Generating AVS scripts...'
$Config['AVS'].Replace('$FilenameIn', $FilenameIn) | Out-File $FilenameAVS -Encoding 'ASCII'
$Config['AVSBitrate'].Replace('$FilenameIn', $FilenameIn).Replace('$FilenameDuration', $FilenameDuration) | Out-File $FilenameAVSBitrate -Encoding 'ASCII'

Write-Host '- Getting bitrate...'
& $ExePath['avsutil'] "$FilenameAVSBitrate"

$FileLengthSeconds = Get-Content "$FilenameDuration"
$file = Get-Item "$FilenameIn"
$FileSize = $file.Length
$BitrateKbps = [int]($FileSize / $FileLengthSeconds * 8 / 1000)
Write-Host "$BitrateKbps kbps"

Write-Host '- Encoding video...'
Write-Host '- Pass 1...'
$Execute = $ExePath['x264'] + ' ' + $Config['x264Pass1'].Replace('$FilenameAVS', $FilenameAVS).Replace('$FilenameStats', $FilenameStats).Replace('$BitrateKbps', $BitrateKbps)
& cmd /c "$Execute"

Write-Host '- Pass 2...'
$Execute = $ExePath['x264'] + ' ' + $Config['x264Pass2'].Replace('$FilenameVideo', $FilenameVideo).Replace('$FilenameAVS', $FilenameAVS).Replace('$FilenameStats', $FilenameStats).Replace('$BitrateKbps', $BitrateKbps)
& cmd /c "$Execute"

'- Encoding audio...'
$Execute = $ExePath['bepipe'] + ' --script "Import(^' + $FilenameAVS + '^)"' + ' | ' + $ExePath['neroaacenc'] + ' ' + $Config['neroaacenc'].Replace('$FilenameAudio', $FilenameAudio)
& cmd /c "$Execute"

Write-Host '- Muxing...'
$Execute = $ExePath['mp4box'] + ' -new -add "' + $FilenameVideo + '" -add "' + $FilenameAudio + '" "' + $FilenameOut + '"'
& cmd /c "$Execute"

Write-Host '- Cleaning up...'
Get-ChildItem $TempPath | ForEach-Object { Remove-Item $_.fullname }

Write-Host '- Done'
}

Write-Host '===Begin Batch Encode==='

$OutPath, $TempPath | ForEach-Object {
if ((Test-Path -PathType 'Container' $_) -eq $False) {
if ((Read-Host "Error: Folder `"$_`" not found, type `"y`" to create it") -eq 'y') {
New-Item $_ -Type Directory | Out-Null
} else {
Write-Host 'Exiting. Edit source file to set up temp and output folders!'
exit
}
}
}

foreach ($Exe in $ExePath.Values) {
if ((Test-Path -PathType 'Leaf' $Exe) -eq $False) {
Write-Host "Error: Executable `"$Exe`" not found."
Write-Host 'Exiting. Edit source file to set up exe paths for x264, bepipe, neroaacenc and mp4box!'
exit
}
}

if ($Path -eq '') {
Write-Host 'Error: No path/file specified'
} elseif (Test-Path -PathType 'Leaf' $Path) {
Encode $Path
} elseif (Test-Path -PathType 'Container' $Path) {
Get-ChildItem ($Path + '*') -Include $FileTypes | ForEach-Object { Encode $_.FullName }
} else {
Write-Host "$Path is not a file/folder"
}

Write-Host '===End Batch Encode==='

stax76
11th January 2010, 07:54
Can it do anything which StaxRip can't do? Why not use a real scripting language like Python or JScript instead of a command shell scripting language? Only benefit PS has for such a script is full COM+ automation and .NET access but other than that it's pretty ugly compared to something elegant like Python.

grv575
11th January 2010, 08:11
Can it do anything which StaxRip can't do? Why not use a real scripting language like Python or JScript instead of a command shell scripting language? Only benefit PS has for such a script is full COM+ automation and .NET access but other than that it's pretty ugly compared to something elegant like Python.

I've checked out every single GUI solution and was only happy with WinFF. But ffmpeg has lamemp3 and rmvb bugs and this gets a better encode since it's 2 pass and figures out a good bitrate to use. Basically automating the bitrate & using 2-pass for quality is the reason.

Powershell since it's built into Vista/Win7. I've done plenty of python stuff but really syntax doesn't make that much of a difference.

stax76
11th January 2010, 08:23
The bitrate calculation should be possible with StaxRip, in the x264 dialog you can define custom switches, whenever a switch appears two times the second one is used by x264. There is a Macro to evaluate a JScript expression that can be used to calculate the bitrate. Another thing that might be used is the command line video encoder. 2 pass is typically used to target size restricted mediums, why don't you not simply use '--crf 20'?

grv575
11th January 2010, 08:43
The bitrate calculation should be possible with StaxRip, in the x264 dialog you can define custom switches, whenever a switch appears two times the second one is used by x264. There is a Macro to evaluate a JScript expression that can be used to calculate the bitrate. Another thing that might be used is the command line video encoder.

Ah, cool. Looks like you can put together the same thing then. Didn't bother doing any advanced scripting with any of the GUIs beyond avisynth scripts.

2 pass is typically used to target size restricted mediums, why don't you not simply use '--crf 20'?

Ongoing discussion:
http://forum.doom9.org/showthread.php?t=151938

Basically because the encodes came out worse.

stax76
11th January 2010, 09:55
StaxRip has also a rich CLI where you can load a template, load file(s) and set the target file size or bitrate etc., the CLI is based on the same command engine the menus use so there are a lot commands available on the CLI. Generally it has rich automation support and allows rich customizations.

creamyhorror
12th January 2010, 10:02
Ongoing discussion:
http://forum.doom9.org/showthread.php?t=151938

Basically because the encodes came out worse.
To save you guys the trouble of clicking, he basically compared a 2-pass XviD encode to an x264 CRF encode without controlling for audio bitrate. And the XviD turned out better. :eek:

RunningSkittle
12th January 2010, 15:44
Whats the point of this? Your script can be simplified immensely (because it doesnt do anything fancy) to just a few lines in NT Batch...

*define vars here*
for /r %%a in (*.whatever) do (goto encode "%%a")
goto end
:encode
x264.exe --pass 1 options %1
x264.exe --pass 2 options %1
goto end
:endEncode

:end

grv575
13th January 2010, 05:53
Whats the point of this? Your script can be simplified immensely (because it doesnt do anything fancy) to just a few lines in NT Batch...

*define vars here*
for /r %%a in (*.whatever) do (goto encode "%%a")
goto end
:encode
x264.exe --pass 1 options %1
x264.exe --pass 2 options %1
goto end
:endEncode

:end


Yeah there's a bat one that's similar at:
http://speeddemosarchive.com/kb/Batch_Encoding

It does pretty much the same thing (uses nero AAC codec for the audio), but you specify the bitrate to use.