]>
code.delx.au - transcoding/blob - encode.py
ca27aaede3e8313d8157638c2dfc1d6e4c80bb48
11 class FatalException(Exception):
15 if re
.match("^[a-zA-Z0-9\-\\.,/@_:=]*$", arg
):
28 def midentify(source
, field
):
29 process
= subprocess
.Popen(
32 "-ao", "null", "-vo", "null",
33 "-frames", "0", "-identify",
35 stdout
=subprocess
.PIPE
,
36 stderr
=subprocess
.PIPE
,
38 for line
in process
.stdout
:
40 key
, value
= line
.split("=")
48 class Command(object):
57 def __init__(self
, profile
, opts
):
58 self
.profile
= profile
61 self
.audio_tmp
= "audio." + self
.codec2exts
[profile
.acodec
]
62 self
.video_tmp
= "video." + self
.codec2exts
[profile
.vcodec
]
67 def print_install_message(self
):
68 print >>sys
.stderr
, "Problem with command: %s", self
.name
70 print >>sys
.stderr
, "Try running:\n# aptitude install %s", self
.package
72 def check_command(self
, cmd
):
75 if subprocess
.Popen(["which", cmd
], stdout
=open("/dev/null", "w")).wait() != 0:
76 raise FatalException("Command '%s' is required" % cmd
)
78 def check_no_file(self
, path
):
79 if os
.path
.exists(path
):
80 raise FatalException("Output file '%s' exists." % path
)
82 def do_exec(self
, args
):
84 print " ".join(map(mkarg
, args
))
86 if subprocess
.Popen(args
).wait() != 0:
87 raise FatalException("Failure executing command: %s" % args
)
90 class MP4Box(Command
):
92 self
.check_command("MP4Box")
93 self
.check_no_file(self
.opts
.output
+ ".mp4")
99 fps
= midentify(self
.video_tmp
, "ID_VIDEO_FPS")
101 output
= self
.opts
.output
+ ".mp4"
105 "-add", self
.video_tmp
,
106 "-add", self
.audio_tmp
,
112 class MKVMerge(Command
):
114 self
.check_command("mkvmerge")
115 self
.check_no_file(self
.opts
.output
+ ".mkv")
121 fps
= midentify(self
.video_tmp
, "ID_VIDEO_FPS")
125 "-o", self
.opts
.output
+ ".mkv",
126 "--default-duration", "0:%sfps"%fps
,
133 class MencoderMux(Command
):
135 self
.check_command("mencoder")
136 self
.check_no_file(self
.opts
.output
+ ".avi")
141 "-o", self
.opts
.output
+ ".avi",
142 "-oac", "copy", "-ovc", "copy",
143 "-noskip", "-mc", "0",
144 "-audiofile", self
.audio_tmp
,
150 class Mencoder(Command
):
152 "xvid": "-xvidencopts",
153 "x264": "-x264encopts",
155 "mp3lame": "-lameopts",
159 if self
.opts
.copyac3
:
160 self
.profile
.acodec
= "copyac3"
161 self
.profile
.aopts
= None
163 def insert_options(self
, cmd
):
164 def try_opt(opt
, var
):
168 if self
.opts
.deinterlace
:
169 cmd
+= ["-vf-add", "pp=lb"]
170 if self
.opts
.detelecine
:
171 self
.opts
.ofps
= "24000/1001"
172 cmd
+= ["-vf-add", "pullup,softskip"]
173 if self
.opts
.copyac3
:
174 cmd
+= ["-noskip", "-mc", "0"]
175 try_opt("-fps", self
.opts
.ifps
)
176 try_opt("-ofps", self
.opts
.ofps
)
177 try_opt("-ss", self
.opts
.startpos
)
178 try_opt("-endpos", self
.opts
.endpos
)
179 try_opt("-dvd-device", self
.opts
.dvd
)
180 try_opt("-chapter", self
.opts
.chapter
)
181 try_opt("-aid", self
.opts
.audioid
)
182 try_opt("-sid", self
.opts
.subtitleid
)
183 try_opt("-vf-add", self
.opts
.vfilters
)
184 try_opt("-af-add", self
.opts
.afilters
)
185 cmd
+= ["-vf-add", "harddup"]
187 def subst_values(self
, cmd
, vpass
):
189 "vbitrate": self
.opts
.vbitrate
,
190 "abitrate": self
.opts
.abitrate
,
194 return [x
% subst
for x
in cmd
]
200 if acodec
== "copyac3":
204 cmd
+= ["mencoder", self
.opts
.input]
205 self
.insert_options(cmd
)
206 cmd
+= ["-ovc", p
.vcodec
, self
.codec2opts
[p
.vcodec
], p
.vopts
]
207 cmd
+= ["-oac", acodec
]
209 cmd
+= [self
.codec2opts
[p
.acodec
], p
.aopts
]
210 cmd
+= self
.profile
.extra1
+ self
.profile
.extra
211 cmd
= self
.subst_values(cmd
, vpass
=n
)
218 cmd
+= ["-o", self
.audio_tmp
, "-of", "rawaudio"]
223 cmd
+= ["-o", self
.video_tmp
, "-of", "rawvideo"]
227 self
.check_command("mencoder")
228 self
.check_no_file(self
.audio_tmp
)
229 self
.check_no_file(self
.video_tmp
)
232 self
.do_exec(self
.pass1())
233 self
.do_exec(self
.pass2())
237 class Profile(object):
238 def __init__(self
, commands
, **kwargs
):
239 self
.default_opts
= {
246 self
.commands
= commands
247 self
.__dict
__.update(kwargs
)
249 def __contains__(self
, keyname
):
250 return hasattr(self
, keyname
)
256 commands
=[Mencoder
, MKVMerge
],
258 vopts
="pass=%(vpass)d:bitrate=%(vbitrate)d:subq=6:frameref=6:me=umh:partitions=all:bframes=4:b_adapt:qcomp=0.7:keyint=250",
260 aopts
="abr:br=%(abitrate)d",
265 commands
=[Mencoder
, MencoderMux
],
267 vopts
="pass=%(vpass)d:bitrate=%(vbitrate)d:vhq=4:autoaspect",
269 aopts
="abr:br=%(abitrate)d",
274 commands
=[Mencoder
, MP4Box
],
276 vopts
="pass=%(vpass)d:bitrate=%(vbitrate)d:me=umh:partitions=all:trellis=1:subq=7:bframes=1:direct_pred=auto",
278 aopts
="br=%(abitrate)d:mpeg=4:object=2",
283 commands
=[Mencoder
, MP4Box
],
285 vopts
="pass=%(vpass)d:bitrate=%(vbitrate)d:vhq=4:autoaspect:max_bframes=0",
287 aopts
="br=%(abitrate)d:mpeg=4:object=2",
288 extra
=["-vf-add", "scale=480:-10"],
293 commands
=[Mencoder
, MP4Box
],
295 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",
297 aopts
="br=%(abitrate)d:mpeg=4:object=2",
298 extra
=["-vf-add", "scale=480:-10"],
299 extra2
=["-channels", "2", "-srate", "48000"],
304 commands
=[Mencoder
, MP4Box
],
310 vopts
="pass=%(vpass)d:bitrate=%(vbitrate)d:vhq=4:autoaspect:max_bframes=0",
312 aopts
="br=%(abitrate)d:mpeg=4:object=2",
313 extra
=["-vf-add", "scale=640:-10"],
321 for profile_name
in profiles
.keys():
322 if sys
.argv
[0].find(profile_name
) >= 0:
325 profile_name
= "xvid"
327 parser
= optparse
.OptionParser(usage
="%prog [options] input [output]")
328 parser
.add_option("--dvd", action
="store", dest
="dvd")
329 parser
.add_option("--deinterlace", action
="store_true", dest
="deinterlace")
330 parser
.add_option("--detelecine", action
="store_true", dest
="detelecine")
331 parser
.add_option("--copyac3", action
="store_true", dest
="copyac3")
332 parser
.add_option("--vfilters", action
="store", dest
="vfilters")
333 parser
.add_option("--afilters", action
="store", dest
="afilters")
334 parser
.add_option("--vbitrate", action
="store", dest
="vbitrate", type="int")
335 parser
.add_option("--abitrate", action
="store", dest
="abitrate", type="int")
336 parser
.add_option("--chapter", action
="store", dest
="chapter")
337 parser
.add_option("--ifps", action
="store", dest
="ifps")
338 parser
.add_option("--ofps", action
="store", dest
="ofps")
339 parser
.add_option("--startpos", action
="store", dest
="startpos")
340 parser
.add_option("--endpos", action
="store", dest
="endpos")
341 parser
.add_option("--audioid", action
="store", dest
="audioid")
342 parser
.add_option("--subtitleid", action
="store", dest
="subtitleid")
343 parser
.add_option("--profile", action
="store", dest
="profile_name", default
=profile_name
)
344 parser
.add_option("--dump", action
="store_true", dest
="dump")
346 opts
, args
= parser
.parse_args(sys
.argv
[1:])
349 output
= os
.path
.splitext(os
.path
.basename(input))[0]
358 if "://" not in input:
359 opts
.input = os
.path
.abspath(input)
362 opts
.dvd
= os
.path
.abspath(opts
.dvd
)
365 opts
.output
= os
.path
.abspath(output
)
376 profile
= profiles
[opts
.profile_name
]
378 print >>sys
.stderr
, "Profile '%s' not found!" % opts
.profile_name
381 # Pull in default option values from the profile
382 for key
, value
in profile
.default_opts
.iteritems():
383 if getattr(opts
, key
) is None:
384 setattr(opts
, key
, value
)
386 # Run in a temp dir so that multiple instances can be run simultaneously
387 tempdir
= tempfile
.mkdtemp()
393 for CommandClass
in profile
.commands
:
394 command
= CommandClass(profile
, opts
)
395 commands
.append(command
)
397 for command
in commands
:
400 except FatalException
, e
:
401 print >>sys
.stderr
, "Error:", str(e
)
406 shutil
.rmtree(tempdir
)
408 if __name__
== "__main__":