n900-encode.py v0.2: switched to mencoder for encoding

This commit is contained in:
seiichiro 2010-05-15 11:39:24 +02:00
parent cc94f36ab8
commit b82de7f927

View file

@ -5,12 +5,16 @@
# Disclaimer: This program is provided without any warranty, USE AT YOUR OWN RISK! # Disclaimer: This program is provided without any warranty, USE AT YOUR OWN RISK!
# #
# (C) 2010 Stefan Brand <seiichiro@seiichiro0185.org> # (C) 2010 Stefan Brand <seiichiro@seiichiro0185.org>
#
# Version 0.2
########################################################################################### ###########################################################################################
import sys, os, getopt, subprocess, re, atexit import sys, os, getopt, subprocess, re, atexit
from signal import signal, SIGTERM, SIGINT from signal import signal, SIGTERM, SIGINT
from time import sleep from time import sleep
version = "0.2"
########################################################################################### ###########################################################################################
# Default values, feel free to adjust # Default values, feel free to adjust
########################################################################################### ###########################################################################################
@ -18,11 +22,12 @@ from time import sleep
_basewidth = 800 # Base width for widescreen Video _basewidth = 800 # Base width for widescreen Video
_basewidth43 = 640 # Base width for 4:3 Video _basewidth43 = 640 # Base width for 4:3 Video
_maxheight = 480 # maximum height allowed _maxheight = 480 # maximum height allowed
_abitrate = 96 # Audio Bitrate in kBit/s _abitrate = 112 # Audio Bitrate in kBit/s
_vbitrate = 500 # Video Bitrate in kBit/s _vbitrate = 22 # Video Bitrate, if set to value < 52 it is used as a CRF Value for Constant rate factor encoding
_threads = 2 # Use n Threads to encode _threads = 0 # Use n Threads to encode (0 means use number of CPUs / Cores)
_mpbin = None # mplayer binary, if set to None it is searched in your $PATH _mpbin = None # mplayer binary, if set to None it is searched in your $PATH
_ffbin = None # ffmpeg binary, if set to None it is searched in your $PATH _mcbin = None # mencoder binary, if set to None it is searched in your $PATH
_m4bin = None # MP4Box binary, if set to None it is searched in your $PATH
########################################################################################### ###########################################################################################
# Main Program, no changes needed below this line # Main Program, no changes needed below this line
@ -41,8 +46,8 @@ def main(argv):
input = None input = None
output = "n900encode.mp4" output = "n900encode.mp4"
mpopts = "" mpopts = ""
abitrate = _abitrate * 1000 abitrate = _abitrate
vbitrate = _vbitrate * 1000 vbitrate = _vbitrate
threads = _threads threads = _threads
overwrite = False overwrite = False
for opt, arg in opts: for opt, arg in opts:
@ -53,9 +58,9 @@ def main(argv):
elif opt in ("-m" "--mpopts"): elif opt in ("-m" "--mpopts"):
mpopts = arg mpopts = arg
elif opt in ("-a", "--abitrate"): elif opt in ("-a", "--abitrate"):
abitrate = int(arg) * 1000 abitrate = int(arg)
elif opt in ("-v", "--vbitrate"): elif opt in ("-v", "--vbitrate"):
vbitrate = int(arg) * 1000 vbitrate = int(arg)
elif opt in ("-t", "--threads"): elif opt in ("-t", "--threads"):
threads = arg threads = arg
elif opt in ("-f", "--force-overwrite"): elif opt in ("-f", "--force-overwrite"):
@ -74,14 +79,24 @@ def main(argv):
print "Error: mplayer not found in PATH and no binary given, Aborting!" print "Error: mplayer not found in PATH and no binary given, Aborting!"
sys.exit(1) sys.exit(1)
global ffbin global mcbin
ffbin = None mcbin = None
if not _ffbin == None and os.path.exists(_ffbin) and not os.path.isdir(_ffbin): if not _mcbin == None and os.path.exists(_mcbin) and not os.path.isdir(_mcbin):
ffbin = _ffbin mcbin = _mcbin
else: else:
ffbin = progpath("ffmpeg") mcbin = progpath("mencoder")
if ffbin == None: if mcbin == None:
print "Error: ffmpeg not found in PATH and no binary given, Aborting!" print "Error: mencoder not found in PATH and no binary given, Aborting!"
sys.exit(1)
global m4bin
m4bin = None
if not _m4bin == None and os.path.exists(_m4bin) and not os.path.isdir(_m4bin):
m4bin = _m4bin
else:
m4bin = progpath("MP4Box")
if m4bin == None:
print "Error: mencoder not found in PATH and no binary given, Aborting!"
sys.exit(1) sys.exit(1)
# Check input and output files # Check input and output files
@ -98,7 +113,7 @@ def main(argv):
# Start Processing # Start Processing
res = calculate(input) res = calculate(input)
convert(input, output, res, abitrate, vbitrate, threads, mpopts) convert(input, output, res, _abitrate, vbitrate, threads, mpopts)
sys.exit(0) sys.exit(0)
@ -119,6 +134,10 @@ def calculate(input):
s = re.compile("^ID_VIDEO_HEIGHT=(.*)$", re.M) s = re.compile("^ID_VIDEO_HEIGHT=(.*)$", re.M)
m = s.search(mp[0]) m = s.search(mp[0])
orig_height = m.group(1) orig_height = m.group(1)
s = re.compile("^ID_VIDEO_FPS=(.*)$", re.M)
m = s.search(mp[0])
fps = m.group(1)
except: except:
print "Error: unable to identify source video, exiting!" print "Error: unable to identify source video, exiting!"
sys.exit(2) sys.exit(2)
@ -132,90 +151,90 @@ def calculate(input):
width = _basewidth43 width = _basewidth43
height = int(round(_basewidth43 / float(orig_aspect) / 16) * 16) height = int(round(_basewidth43 / float(orig_aspect) / 16) * 16)
return (width, height) return (width, height, fps)
def convert(input, output, res, abitrate, vbitrate, threads, mpopts): def convert(input, output, res, abitrate, vbitrate, threads, mpopts):
"""Convert the Video""" """Convert the Video"""
# Needed for cleanup function # Needed for cleanup function
global afifo, vfifo, mda, mdv global h264, aac
# Create FIFOs for passing audio/video from mplayer to ffmpeg # define some localvariables
pid = os.getpid() pid = os.getpid()
afifo = "/tmp/stream" + str(pid) + ".wav" h264 = output + ".h264"
vfifo = "/tmp/stream" + str(pid) + ".yuv" aac = output + ".aac"
os.mkfifo(afifo) width = str(res[0])
os.mkfifo(vfifo) height = str(res[1])
fps = str(res[2])
# Define mplayer command for video decoding if (vbitrate < 52):
mpvideodec = [ mpbin, vbr = "crf=" + str(vbitrate)
else:
vbr = "bitrate=" + str(vbitrate)
# Define mencoder command for video encoding
mencvideo = [ mcbin,
"-ovc", "x264",
"-x264encopts", vbr + ":bframes=0:trellis=0:nocabac:no8x8dct:level_idc=30:frameref=4:me=umh:weightp=0:vbv_bufsize=2000:vbv_maxrate=1800:threads=" + str(threads),
"-sws", "9", "-sws", "9",
"-vf", "scale=" + str(res[0]) + ":" + str(res[1]) + ",unsharp=c4x4:0.3:l5x5:0.5", "-vf", "scale=" + width + ":" + height + ",dsize=" + width + ":" + height + ",fixpts=fps=" + fps + ",ass,fixpts,harddup",
"-vo", "yuv4mpeg:file=" + vfifo, "-of", "rawvideo",
"-ao", "null", "-o", h264,
"-nosound", "-nosound",
"-noframedrop", "-noskip",
"-benchmark", "-ass",
"-quiet",
"-nolirc",
"-msglevel", "all=-1",
input ] input ]
for mpopt in mpopts.split(" "): if (mpopts != ""):
mpvideodec.append(mpopt) for mpopt in mpopts.split(" "):
mencvideo.append(mpopt)
# Define mplayer command for audio decoding # Define mencoder command for audio encoding
mpaudiodec = [ mpbin, mencaudio = [ mcbin,
"-ao", "pcm:file=" + afifo, "-oac", "faac",
"-vo", "null", "-faacopts", "br=" + str(abitrate) + ":mpeg=4:object=2",
"-vc", "null", "-ovc", "copy",
"-noframedrop", "-of", "rawaudio",
"-quiet", "-o", aac,
"-nolirc", "-noskip",
"-msglevel", "all=-1",
input ] input ]
for mpopt in mpopts.split(" "): if (mpopts != ""):
mpaudiodec.append(mpopt) for mpopt in mpopts.split(" "):
mencaudio.append(mpopt)
# Define ffmpeg command for a/v encoding # Define MB4Box Muxing Command
ffmenc = [ ffbin, mp4mux = [ m4bin,
"-f", "yuv4mpegpipe", "-fps", fps,
"-i", vfifo, "-new", "-add", h264,
"-i", afifo, "-add", aac,
"-acodec", "libfaac",
"-ac", "2",
"-ab", str(abitrate),
"-ar", "22500",
"-vcodec", "libx264",
"-threads", str(threads),
"-b", str(vbitrate),
"-flags", "+loop", "-cmp", "+chroma",
"-partitions", "+parti4x4+partp8x8+partb8x8",
"-subq", "5", "-trellis", "1", "-refs", "1",
"-coder", "0", "-me_range", "16",
"-g", "300", "-keyint_min", "25",
"-sc_threshold", "40", "-i_qfactor", "0.71",
"-bt", "640", "-bufsize", "10M", "-maxrate", "1000000",
"-rc_eq", "'blurCplx^(1-qComp)'",
"-qcomp", "0.62", "-qmin", "10", "-qmax", "51",
"-level", "30", "-f", "mp4",
output ] output ]
# Start mplayer decoding processes in background # Encode Video
print "### Starting Video Encode ###"
try: try:
mdv = subprocess.Popen(mpvideodec, stdout=None, stderr=None) subprocess.check_call(mencvideo)
mda = subprocess.Popen(mpaudiodec, stdout=None, stderr=None) print "### Video Encoding Finished. ###"
except: except subprocess.CalledProcessError:
print "Error: Starting decoding threads failed!" print "Error: Video Encoding Failed!"
sys.exit(3) sys.exit(3)
# Start ffmpeg encoding process in foreground print "### Starting Audio Encode ###"
try: try:
subprocess.check_call(ffmenc) subprocess.check_call(mencaudio)
print "### Audio Encode Finished. ###"
except subprocess.CalledProcessError:
print "Error: Audio Encoding Failed!"
sys.exit(3)
# Mux Video and Audio to MP4
print "### Starting MP4 Muxing ###"
try:
subprocess.check_call(mp4mux)
print "### MP4 Muxing Finished. Video is now ready! ###"
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
print "Error: Encoding thread failed!" print "Error: Encoding thread failed!"
sys .exit(4) sys.exit(4)
def progpath(program): def progpath(program):
@ -229,25 +248,18 @@ def progpath(program):
def cleanup(): def cleanup():
"""Clean up when killed""" """Clean up when killed"""
# give ffmpeg time to stop
sleep(2)
# Cleanup # Cleanup
try: try:
os.kill(mda.pid()) os.remove(h264)
os.kill(mdv.pid()) os.remove(aac)
finally: finally:
try: sys.exit(0)
os.remove(afifo)
os.remove(vfifo)
finally:
sys.exit(0)
def usage(): def usage():
"""Print avaiable commandline arguments""" """Print avaiable commandline arguments"""
print "This is n900-encode.py (C) 2010 Stefan Brand <seiichiro0185 AT tol DOT ch>" print "This is n900-encode.py Version" + version + "(C) 2010 Stefan Brand <seiichiro0185 AT tol DOT ch>"
print "Usage:" print "Usage:"
print " n900-encode.py --input <file> [opts]\n" print " n900-encode.py --input <file> [opts]\n"
print "Options:" print "Options:"
@ -255,8 +267,8 @@ def usage():
print " --output <file> [-o]: Name of the converted Video" print " --output <file> [-o]: Name of the converted Video"
print " --mpopts \"<opts>\" [-m]: Additional options for mplayer (eg -sid 1 or -aid 1) Must be enclosed in \"\"" print " --mpopts \"<opts>\" [-m]: Additional options for mplayer (eg -sid 1 or -aid 1) Must be enclosed in \"\""
print " --abitrate <br> [-a]: Audio Bitrate in KBit/s" print " --abitrate <br> [-a]: Audio Bitrate in KBit/s"
print " --vbitrate <br> [-v]: Video Bitrate in kBit/s" print " --vbitrate <br> [-v]: Video Bitrate in kBit/s, values < 0 activate h264 CRF-Encoding, given value is used as CRF Factor"
print " --threads <num> [-t]: Use <num> Threads to encode" print " --threads <num> [-t]: Use <num> Threads to encode, giving 0 will autodetect number of CPUs"
print " --force-overwrite [-f]: Overwrite output-file if existing" print " --force-overwrite [-f]: Overwrite output-file if existing"
print " --help [-h]: Print this Help" print " --help [-h]: Print this Help"
sys.exit(0) sys.exit(0)