]> code.delx.au - transcoding/blobdiff - encode.py
Fixed fps check to work without X11
[transcoding] / encode.py
index fceca4184fe79d9e40f7bf2b554c0f23b9b5e39f..c788bb632d8df67107b85633068dfe5d61a4289e 100755 (executable)
--- 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