X-Git-Url: https://code.delx.au/transcoding/blobdiff_plain/fbb2d992b825bdcecbfd0da160170f36be81a8b1..a9536f5dc5e382234c9ac8dc5a59a441f7fcf594:/encode.py diff --git a/encode.py b/encode.py index fceca41..c788bb6 100755 --- a/encode.py +++ b/encode.py @@ -25,10 +25,40 @@ def mkarg(arg): out += "\"" return out +def midentify(source, field): + process = subprocess.Popen( + [ + "mplayer", source, + "-ao", "null", "-vo", "null", + "-frames", "0", "-identify", + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + for line in process.stdout: + try: + key, value = line.split("=") + except ValueError: + continue + if key == field: + return value.strip() + + + class Command(object): + codec2exts = { + "xvid": "m4v", + "x264": "h264", + "faac": "aac", + "mp3lame": "mp3", + "copyac3": "ac3", + } + def __init__(self, profile, opts): self.profile = profile self.opts = opts + self.audio_tmp = "audio." + self.codec2exts[profile.acodec] + self.video_tmp = "video." + self.codec2exts[profile.vcodec] def print_install_message(self): print >>sys.stderr, "Problem with command: %s", self.name @@ -54,51 +84,24 @@ class Command(object): class MP4Box(Command): - codec2exts = { - "xvid": "m4v", - "x264": "h264", - "faac": "aac", - } - def check(self): - self.check_command("mencoder") self.check_command("MP4Box") self.check_no_file(self.opts.output + ".mp4") def run(self): - p = self.profile - input = self.opts.output + ".avi" # From Mencoder command - output = self.opts.output + ".mp4" + if self.opts.dump: + fps = "???" + else: + fps = midentify(self.video_tmp, "ID_VIDEO_FPS") - # Check FPS - fps = "???" - if not self.opts.dump: - process = subprocess.Popen( - ["mplayer", "-frames", "0", "-identify", input], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - for line in process.stdout: - try: - key, value = line.split("=") - except ValueError: - continue - if key == "ID_VIDEO_FPS": - fps = value - - # Strip out video & audio - video = "video.%s" % self.codec2exts[p.vcodec] - audio = "audio.%s" % self.codec2exts[p.acodec] - mencoder = ["mencoder", input, "-ovc", "copy", "-oac", "copy", "-of"] - self.do_exec(["rm", "-f", output]) - self.do_exec(mencoder + ["rawvideo", "-o", video]) - self.do_exec(mencoder + ["rawaudio", "-o", audio]) - - # Mux them back together - self.do_exec(["MP4Box", "-add", video, "-add", audio, "-fps", fps, output]) - - # Clean up temp files - self.do_exec(["rm", "-f", video, audio, input]) + output = self.opts.output + ".mp4" + self.do_exec([ + "MP4Box", + "-fps", fps, + "-add", self.video_tmp, + "-add", self.audio_tmp, + output + ]) @@ -108,10 +111,35 @@ class MKVMerge(Command): self.check_no_file(self.opts.output + ".mkv") def run(self): - input = self.opts.output + ".avi" # From Mencoder command - output = self.opts.output + ".mkv" - self.do_exec(["mkvmerge", "-o", output, input]) - self.do_exec(["rm", "-f", input]) + if self.opts.dump: + fps = "???" + else: + fps = midentify(self.video_tmp, "ID_VIDEO_FPS") + + self.do_exec([ + "mkvmerge", + "-o", self.opts.output + ".mkv", + "--default-duration", "0:%sfps"%fps, + self.video_tmp, + self.audio_tmp, + ]) + + + +class MencoderMux(Command): + def check(self): + self.check_command("mencoder") + self.check_no_file(self.opts.output + ".avi") + + def run(self): + self.do_exec([ + "mencoder", + "-o", self.opts.output + ".avi", + "-oac", "copy", "-ovc", "copy", + "-noskip", "-mc", "0", + "-audiofile", self.audio_tmp, + self.video_tmp, + ]) @@ -143,47 +171,53 @@ class Mencoder(Command): try_opt("-sid", self.opts.subtitleid) try_opt("-vf-add", self.opts.vfilters) try_opt("-af-add", self.opts.afilters) + cmd += ["-vf-add", "harddup"] def subst_values(self, cmd, vpass): subst = { "vbitrate": self.opts.vbitrate, "abitrate": self.opts.abitrate, - "input": self.opts.input, - "output": self.opts.output + ".avi", "vpass": vpass, } return [x % subst for x in cmd] - - def pass1(self): + + def passn(self, n): p = self.profile + + acodec = p.acodec + if self.opts.copyac3: + acodec = "copy" + p.acodec = "copyac3" + p.aopts = None + cmd = [] - cmd += ["mencoder", "%(input)s", "-o", "/dev/null"] + cmd += ["mencoder", self.opts.input] self.insert_options(cmd) cmd += ["-ovc", p.vcodec, self.codec2opts[p.vcodec], p.vopts] - cmd += ["-oac", "copy"] + cmd += ["-oac", acodec] + if p.aopts: + cmd += [self.codec2opts[p.acodec], p.aopts] cmd += self.profile.extra1 + self.profile.extra - cmd = self.subst_values(cmd, vpass=1) + cmd = self.subst_values(cmd, vpass=n) + + return cmd + + + def pass1(self): + cmd = self.passn(1) + cmd += ["-o", self.audio_tmp, "-of", "rawaudio"] return cmd def pass2(self): - p = self.profile - cmd = [] - cmd += ["mencoder", "%(input)s", "-o", "%(output)s"] - self.insert_options(cmd) - cmd += ["-ovc", p.vcodec, self.codec2opts[p.vcodec], p.vopts] - cmd += ["-oac", p.acodec] - if "aopts" in p: - cmd += [self.codec2opts[p.acodec], p.aopts] - if self.opts.episode_name: - cmd += ["-info", "name='%s'" % self.opts.episode_name] - cmd += self.profile.extra2 + self.profile.extra - cmd = self.subst_values(cmd, vpass=2) + cmd = self.passn(2) + cmd += ["-o", self.video_tmp, "-of", "rawvideo"] return cmd def check(self): self.check_command("mencoder") - self.check_no_file(self.opts.output + ".avi") + self.check_no_file(self.audio_tmp) + self.check_no_file(self.video_tmp) def run(self): self.do_exec(self.pass1()) @@ -215,17 +249,15 @@ profiles = { vopts="pass=%(vpass)d:bitrate=%(vbitrate)d:subq=6:frameref=6:me=umh:partitions=all:bframes=4:b_adapt:qcomp=0.7:keyint=250", acodec="mp3lame", aopts="abr:br=%(abitrate)d", - extra=["-vf-add", "harddup"], ), "xvid" : Profile( - commands=[Mencoder], + commands=[Mencoder, MencoderMux], vcodec="xvid", vopts="pass=%(vpass)d:bitrate=%(vbitrate)d:vhq=4:autoaspect", acodec="mp3lame", aopts="abr:br=%(abitrate)d", - extra2=["-ffourcc", "DX50"], ), "apple-quicktime" : @@ -235,7 +267,6 @@ profiles = { vopts="pass=%(vpass)d:bitrate=%(vbitrate)d:me=umh:partitions=all:trellis=1:subq=7:bframes=1:direct_pred=auto", acodec="faac", aopts="br=%(abitrate)d:mpeg=4:object=2", - extra=["-vf-add", "harddup"], ), "ipod-xvid" : @@ -245,7 +276,7 @@ profiles = { vopts="pass=%(vpass)d:bitrate=%(vbitrate)d:vhq=4:autoaspect:max_bframes=0", acodec="faac", aopts="br=%(abitrate)d:mpeg=4:object=2", - extra=["-vf-add", "scale=480:-10,harddup"], + extra=["-vf-add", "scale=480:-10"], ), "ipod-x264" : @@ -255,7 +286,7 @@ profiles = { vopts="pass=%(vpass)d:bitrate=%(vbitrate)d:vbv_maxrate=1500:vbv_bufsize=2000:nocabac:me=umh:partitions=all:trellis=1:subq=7:bframes=0:direct_pred=auto:level_idc=30:turbo", acodec="faac", aopts="br=%(abitrate)d:mpeg=4:object=2", - extra=["-vf-add", "scale=480:-10,harddup"], + extra=["-vf-add", "scale=480:-10"], extra2=["-channels", "2", "-srate", "48000"], ), @@ -263,14 +294,14 @@ profiles = { Profile( commands=[Mencoder, MP4Box], default_opts={ - "vbitrate": 800, - "abitrate": 96, + "vbitrate": 256, + "abitrate": 64, }, vcodec="xvid", vopts="pass=%(vpass)d:bitrate=%(vbitrate)d:vhq=4:autoaspect:max_bframes=0", acodec="faac", aopts="br=%(abitrate)d:mpeg=4:object=2", - extra=["-vf-add", "scale=640:-10,harddup"], + extra=["-vf-add", "scale=640:-10"], ), } @@ -288,6 +319,7 @@ def parse_args(): parser.add_option("--dvd", action="store", dest="dvd") parser.add_option("--deinterlace", action="store_true", dest="deinterlace") parser.add_option("--detelecine", action="store_true", dest="detelecine") + parser.add_option("--copyac3", action="store_true", dest="copyac3") parser.add_option("--vfilters", action="store", dest="vfilters") parser.add_option("--afilters", action="store", dest="afilters") parser.add_option("--vbitrate", action="store", dest="vbitrate", type="int") @@ -300,7 +332,6 @@ def parse_args(): parser.add_option("--audioid", action="store", dest="audioid") parser.add_option("--subtitleid", action="store", dest="subtitleid") parser.add_option("--profile", action="store", dest="profile_name", default=profile_name) - parser.add_option("--episode-name", action="store", dest="episode_name") parser.add_option("--dump", action="store_true", dest="dump") try: opts, args = parser.parse_args(sys.argv[1:]) @@ -327,6 +358,8 @@ def parse_args(): return opts def main(): + os.nice(1) + opts = parse_args() # Find our profile