From b82de7f9276967efa0ca4e0f5b4efcf34e241c18 Mon Sep 17 00:00:00 2001 From: Stefan Brand Date: Sat, 15 May 2010 11:39:24 +0200 Subject: [PATCH] n900-encode.py v0.2: switched to mencoder for encoding --- n900-encode.py | 192 ++++++++++++++++++++++++++----------------------- 1 file changed, 102 insertions(+), 90 deletions(-) diff --git a/n900-encode.py b/n900-encode.py index 55871d8..e7c2047 100755 --- a/n900-encode.py +++ b/n900-encode.py @@ -5,12 +5,16 @@ # Disclaimer: This program is provided without any warranty, USE AT YOUR OWN RISK! # # (C) 2010 Stefan Brand +# +# Version 0.2 ########################################################################################### import sys, os, getopt, subprocess, re, atexit from signal import signal, SIGTERM, SIGINT from time import sleep +version = "0.2" + ########################################################################################### # Default values, feel free to adjust ########################################################################################### @@ -18,11 +22,12 @@ from time import sleep _basewidth = 800 # Base width for widescreen Video _basewidth43 = 640 # Base width for 4:3 Video _maxheight = 480 # maximum height allowed -_abitrate = 96 # Audio Bitrate in kBit/s -_vbitrate = 500 # Video Bitrate in kBit/s -_threads = 2 # Use n Threads to encode +_abitrate = 112 # Audio 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 = 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 -_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 @@ -41,8 +46,8 @@ def main(argv): input = None output = "n900encode.mp4" mpopts = "" - abitrate = _abitrate * 1000 - vbitrate = _vbitrate * 1000 + abitrate = _abitrate + vbitrate = _vbitrate threads = _threads overwrite = False for opt, arg in opts: @@ -53,9 +58,9 @@ def main(argv): elif opt in ("-m" "--mpopts"): mpopts = arg elif opt in ("-a", "--abitrate"): - abitrate = int(arg) * 1000 + abitrate = int(arg) elif opt in ("-v", "--vbitrate"): - vbitrate = int(arg) * 1000 + vbitrate = int(arg) elif opt in ("-t", "--threads"): threads = arg 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!" sys.exit(1) - global ffbin - ffbin = None - if not _ffbin == None and os.path.exists(_ffbin) and not os.path.isdir(_ffbin): - ffbin = _ffbin + global mcbin + mcbin = None + if not _mcbin == None and os.path.exists(_mcbin) and not os.path.isdir(_mcbin): + mcbin = _mcbin else: - ffbin = progpath("ffmpeg") - if ffbin == None: - print "Error: ffmpeg not found in PATH and no binary given, Aborting!" + mcbin = progpath("mencoder") + if mcbin == None: + 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) # Check input and output files @@ -98,7 +113,7 @@ def main(argv): # Start Processing res = calculate(input) - convert(input, output, res, abitrate, vbitrate, threads, mpopts) + convert(input, output, res, _abitrate, vbitrate, threads, mpopts) sys.exit(0) @@ -119,6 +134,10 @@ def calculate(input): s = re.compile("^ID_VIDEO_HEIGHT=(.*)$", re.M) m = s.search(mp[0]) orig_height = m.group(1) + s = re.compile("^ID_VIDEO_FPS=(.*)$", re.M) + m = s.search(mp[0]) + fps = m.group(1) + except: print "Error: unable to identify source video, exiting!" sys.exit(2) @@ -132,90 +151,90 @@ def calculate(input): width = _basewidth43 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): """Convert the Video""" # 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() - afifo = "/tmp/stream" + str(pid) + ".wav" - vfifo = "/tmp/stream" + str(pid) + ".yuv" - os.mkfifo(afifo) - os.mkfifo(vfifo) + h264 = output + ".h264" + aac = output + ".aac" + width = str(res[0]) + height = str(res[1]) + fps = str(res[2]) - # Define mplayer command for video decoding - mpvideodec = [ mpbin, + if (vbitrate < 52): + 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", - "-vf", "scale=" + str(res[0]) + ":" + str(res[1]) + ",unsharp=c4x4:0.3:l5x5:0.5", - "-vo", "yuv4mpeg:file=" + vfifo, - "-ao", "null", + "-vf", "scale=" + width + ":" + height + ",dsize=" + width + ":" + height + ",fixpts=fps=" + fps + ",ass,fixpts,harddup", + "-of", "rawvideo", + "-o", h264, "-nosound", - "-noframedrop", - "-benchmark", - "-quiet", - "-nolirc", - "-msglevel", "all=-1", + "-noskip", + "-ass", input ] - for mpopt in mpopts.split(" "): - mpvideodec.append(mpopt) + if (mpopts != ""): + for mpopt in mpopts.split(" "): + mencvideo.append(mpopt) - # Define mplayer command for audio decoding - mpaudiodec = [ mpbin, - "-ao", "pcm:file=" + afifo, - "-vo", "null", - "-vc", "null", - "-noframedrop", - "-quiet", - "-nolirc", - "-msglevel", "all=-1", + # Define mencoder command for audio encoding + mencaudio = [ mcbin, + "-oac", "faac", + "-faacopts", "br=" + str(abitrate) + ":mpeg=4:object=2", + "-ovc", "copy", + "-of", "rawaudio", + "-o", aac, + "-noskip", input ] - for mpopt in mpopts.split(" "): - mpaudiodec.append(mpopt) + if (mpopts != ""): + for mpopt in mpopts.split(" "): + mencaudio.append(mpopt) - # Define ffmpeg command for a/v encoding - ffmenc = [ ffbin, - "-f", "yuv4mpegpipe", - "-i", vfifo, - "-i", afifo, - "-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", + # Define MB4Box Muxing Command + mp4mux = [ m4bin, + "-fps", fps, + "-new", "-add", h264, + "-add", aac, output ] - - # Start mplayer decoding processes in background + + # Encode Video + print "### Starting Video Encode ###" try: - mdv = subprocess.Popen(mpvideodec, stdout=None, stderr=None) - mda = subprocess.Popen(mpaudiodec, stdout=None, stderr=None) - except: - print "Error: Starting decoding threads failed!" + subprocess.check_call(mencvideo) + print "### Video Encoding Finished. ###" + except subprocess.CalledProcessError: + print "Error: Video Encoding Failed!" sys.exit(3) - # Start ffmpeg encoding process in foreground + print "### Starting Audio Encode ###" 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: print "Error: Encoding thread failed!" - sys .exit(4) + sys.exit(4) def progpath(program): @@ -229,25 +248,18 @@ def progpath(program): def cleanup(): """Clean up when killed""" - # give ffmpeg time to stop - sleep(2) - # Cleanup try: - os.kill(mda.pid()) - os.kill(mdv.pid()) + os.remove(h264) + os.remove(aac) finally: - try: - os.remove(afifo) - os.remove(vfifo) - finally: - sys.exit(0) + sys.exit(0) def usage(): """Print avaiable commandline arguments""" - print "This is n900-encode.py (C) 2010 Stefan Brand " + print "This is n900-encode.py Version" + version + "(C) 2010 Stefan Brand " print "Usage:" print " n900-encode.py --input [opts]\n" print "Options:" @@ -255,8 +267,8 @@ def usage(): print " --output [-o]: Name of the converted Video" print " --mpopts \"\" [-m]: Additional options for mplayer (eg -sid 1 or -aid 1) Must be enclosed in \"\"" print " --abitrate
[-a]: Audio Bitrate in KBit/s" - print " --vbitrate
[-v]: Video Bitrate in kBit/s" - print " --threads [-t]: Use Threads to encode" + print " --vbitrate
[-v]: Video Bitrate in kBit/s, values < 0 activate h264 CRF-Encoding, given value is used as CRF Factor" + print " --threads [-t]: Use Threads to encode, giving 0 will autodetect number of CPUs" print " --force-overwrite [-f]: Overwrite output-file if existing" print " --help [-h]: Print this Help" sys.exit(0)